calling retain, retainCount analyzing EXC_BAD_ACCESS with release - objective-c

I'm a java programmer new to Objective C, so please be gentle :)
I'getting an errorr message saying EXC_BAD_ACCESS on calling release on an object:
I read documentation and threads on this site, but I see data that's confusing me
- (void) dealloc {
NSLog(#"dealloc in image Retain count: %i", [image retainCount]);
[image release];//method throwing EXC_BAD_ACCESS
..............
}
the logged retain count is: 1
In the code that is causing the dealloc I have:
UIImage *scrn = [[UIImage alloc] initWithCGImage:newImage];
NSLog(#"in after instantiation Retain count: %i", [scrn retainCount]);// logs retain count of 1
CGImageRelease(newImage);
Decoder *d = [[Decoder alloc] init];
.....
NSLog(#"in before decoding Retain count: %i", [scrn retainCount]);// logs retain count of 1
decoding = [d decodeImage:scrn cropRect:cropRect] == YES ? NO : YES;
NSLog(#"in after decoding Retain count: %i", [scrn retainCount]); // logs retain count of 2
[d release]; // this line causes invocation of dealloc on the previous code sniplet
[scrn release];
In decodeImage the following is going on:
- (BOOL) decodeImage:(UIImage *)i cropRect:(CGRect)cr {
NSLog(#"Decoder.mm.decodeImage initial Retain count i : %i retaincount image %i", [i retainCount], [image retainCount]); //logs: Decoder.mm.decodeImage initial Retain count i : 1 retaincount image 0
[self setImage: i];
NSLog(#"Decoder.mm.decodeImage after setting image Retain count i : %i retaincount image %i", [i retainCount], [image retainCount]);//logs: Decoder.mm.decodeImage after setting image Retain count i : 2 retaincount image 2
.......
return [self decode];
}
There are several things puzzeling me:
From what understood the retainCount is increased by calling retain or by instantiating a new object, not by assigningment of one var to another as is done in self setImage: i]; however i see that the retaincount is increased by one
Before calling [d release] the logged retainCount is 2, in the method dealloc the count is 1
If the count is 1, why do I get the EXC_BAD_ACCESS ???
Edit: added additional code as requested
#implementation Decoder
#synthesize image;
The setting of image is mentioned in the third code sniplet above.

Retain count can also be incremented by the system (for build-in types) and by properties that are defined with the retain or copy attributes. You're only responsible for the ones you cause (not the system retains), but don't depend on the retain count when trying to determine why you're getting EXC_BAD_ACCESS. XCode has some good build-in analysis tools that are better for tracking down access errors.
One important thing to note: your retaincount will never go below 1 even if you release when the count is 1.
See this question for good information on retaincount.

Related

Reference Count or Retain Count Issue (Memory Management)

The below code works fine, you can run it at your system for confirmation.
My question is, as you can see, the dealloc method is only called when the retain count reaches zero, meaning the memory is freed for the RetainTracker object. However, the issue is when I log the retain count in dealloc method, it still shows a retain count of 1. Why is this?
Here is my code:
#import <Foundation/Foundation.h>
#interface RetainTracker : NSObject
#end
#implementation RetainTracker
- (id)init {
if (self = [super init]) {
NSLog(#"init: Retain count of %lu",(unsigned long)[self retainCount]);
}
return self;
}
- (void)dealloc {
NSLog(#"Dealloc called bye bye!==>%lu",(unsigned long)self.retainCount);
[super dealloc];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
RetainTracker *myRetainTracker = [RetainTracker new];
[myRetainTracker retain]; // count-->2
NSLog(#"The retain count is ==>%lu",(unsigned long)myRetainTracker.retainCount);
[myRetainTracker release];// count -->1
NSLog(#"The retain count is ==>%lu",(unsigned long)myRetainTracker.retainCount);
[myRetainTracker retain];// count -->2
NSLog(#"The retain count is ==>%lu",(unsigned long)myRetainTracker.retainCount);
[myRetainTracker release];// count -->1
NSLog(#"The retain count is ==>%lu",(unsigned long)myRetainTracker.retainCount);
[myRetainTracker release];// count -->0
NSLog(#"The retain count is ==>%lu",(unsigned long)myRetainTracker.retainCount);
}
return 0;
}
Here are the logs:
init: Retain count of 1
The retain count is ==>2
The retain count is ==>1
The retain count is ==>2
The retain count is ==>1
Dealloc called bye bye!==>1
The retain count is ==>1
This is not surprising, the release code probably looks like this (in pseudocode):
- (void)release {
if (retainCount > 1) {
retainCount -= 1
} else {
// no need to set the retainCount to 0 here,
// the object now ends its existence
[self dealloc]
}
}
Also, your last NSLog is actually accessing an object that no longer exists which can result in a crash.
Note that the value read from retainCount should never be relied upon. It's just an implementation detail. It is much safer to think about retain and release as transfer of ownership.
From retainCount documentation:
Do not use this method.
and
This method is of no value in debugging memory management issues. (...) it is very unlikely that you can get useful information from this method.
Switch your project to ARC. Seriously. I have forgotten everything I knew about retain / release three years ago. Other than that, the retain count of an object in the process of being released is meaningless. It's pointless wondering about it.

Why does the reference count when retrieving object from container not increase?

I have created a little test project to try to resolve a problem I am having in my main project. I've noticed that when retrieving an object from a container the reference count dosen't increment.
I am confused why this is not the case?
For example this code will not increase the reference count of the hereDoggy object:
//Retrieve the dog, why does this not increment the reference count?
Dog* hereDoggy = [cont1 objectAtIndex:0];
Below is the full example:
-(void)doZombieProblem
{
NSMutableArray* cont1 = [NSMutableArray array];
NSMutableArray* cont2 = [NSMutableArray array];
NSMutableArray* cont3 = nil;
//Create the dog pointer
Dog* doggy = [[Dog alloc] initWithName:#"Bernard"];
//Add to container1
[cont1 addObject:doggy];
//Release the dog pointer
[doggy release];
while ([cont1 count] > 0)
{
//Retrieve the dog, why does this not increment the reference count?
Dog* hereDoggy = [cont1 objectAtIndex:0];
//Add it to cont2
[cont2 addObject:hereDoggy];
//Remove it from cont1.
[cont1 removeObjectAtIndex:0];
//No need to release as we haven't increased the reference count.
//[hereDoggy release];
}
//I should be able to retrieve the dog here from cont2.
Dog* bernard = [cont2 objectAtIndex:0];
//No need to release as we haven't increased the reference count.
//[bernard release];
}
In this case, if you want to increase the retain count for your object you need to send a retain (or a copy) message.
As a rule of thumb
You need always to balance your retains (or copyies) with your releases. If you don't do it you can have memory leaks. Otherwise switch to the ARC feature to avoid the code amount to write and simplify your life.
Here a useful link to understand how Memory Management works.
MemoryMgmt
I commented your code to understand what is going on:
// the object referenced by doggy has a retain count of 1
Dog* doggy = [[Dog alloc] initWithName:#"Bernard"];
// now the retain count is 2 since you added to a container class like NSArray
[cont1 addObject:doggy];
// now the retain count is 1
[doggy release];
Then, within the while statement:
// the retain count still remains 1
Dog* hereDoggy = [cont1 objectAtIndex:0];
// the retain count increases to 2
[cont2 addObject:hereDoggy];
// the retain count goes to 1
[cont1 removeObjectAtIndex:0];
Since, the object is maintained alive by cont2 you are able to access it.
If you do [cont2 removeObjectAtIndex:0]; the retain count reaches 0 and the object is deallocated automatically.
It's your responsibility as the user of the object to manage it's retain count. This is because only you, the consumer, know when you are done with it. That's why just calling [cont1 objectAtIndex:0] doesn't increment it. NSArray has no clue what you have planned with the object it returns.
Think of retain count to indicate the number of things owning something. When it's 0, no one owns it, so let it be garbage collected. If it's 1, then only 1 thing needs it/owns it (and on up).
When you call [cont1 addObject:doggy] NSMutableArray will absolutely increment the retain count on it (behind the scenes), just like when you call [cont1 removeObjectAtIndex:0] NSMutableArray will decrement the retain count on it.
Now, if you need hereDoggy for any period of time, just call retain on it yourself, and then release where appropriate.

Releasing local variables before return?

In objective-c, I understand that you need to release anything you init/retain/copy. Do I need to do that before a return statement? I'm wanting to understand calling release explicitly and not use autorelease.
-(void) someMethod
{
AnotherClass* ac = [[AnotherClass alloc] init];
if([ac somethingHappens]){
// Do I need to release ac here?
return;
}
[ac doSomethingElse];
[ac release];
}
Thanks!
Yes, you need to release your variables, however you exit from the method.
It's pretty straight-forward: when you init something the retain count is incremented. When you release it's decremented. When it reaches zero it's automatically deallocated (freed).
In your code above, you init the variable but if it follows the return route then the variables retain count never gets to zero and, therefore, is never deallocated.
Suppose to have a local variable assigned like the following
NSString *placeHolder = [NSString stringWithFormat:#"%# %#",[someObject value1], [someObject value2]];
Now pass this variable to a object defined method, such as setPlaceholder of UISearchBar object
[self.theSearchBar setPlaceholder:placeHolder];
How to release in the right way the assigned string 'placeHolder' ?
If you suppose to autoreleas it:
NSString *placeHolder = [[NSString stringWithFormat:#"%# %#",[someObject value1], [someObject value2]] autorelease];
your code will fail with a bad_exc_access
If you think to release the variable after passed to somewhere else like
[self.theSearchBar setPlaceholder:placeHolder];
[placeHolder release];
a runtime exception will throw too.
So what's wrong?
The problem is the retain count. The UISearchBar object is allocated yet, so if you release or auto-release such a variable, referred by that object, the retain count is still the same
NSLog(#"Retain count before assign to refer other object %d", [placeHolder retainCount]);
[self.theSearchBar setPlaceholder:placeHolder];
NSLog(#"Retain count after referencing %d", [placeHolder retainCount]);
So, how to handle this?
Try something like the following
[placeHolder retain]; // retainCount +1
[self.theSearchBar setPlaceholder:placeHolder];
[placeHolder release]; // retainCount -1
What we did than ? Let's have a look at the retain count now
NSLog(#"Retain count before doing retain %d", [placeHolder retainCount]);
[placeHolder retain]; // retainCount +1
NSLog(#"Retain count after retaining it %d", [placeHolder retainCount]);
So, we incremented the retain count before assign it (get referenced by) to some object, and - after that - we release locally that variable.
That's all.

Objective-C code not deallocating memory!

I'm trying to learn Objective-C. I almost finished one excercise but it is not deallocating the memory:
This is is what I have:
void PrintPolygonInfo() {
NSLog(#"--------------------");
NSLog(#" PRINT POLYGON INFO");
NSLog(#"--------------------");
NSMutableArray *array = [[NSMutableArray alloc] init];
PolygonShape *p1 = [[PolygonShape alloc] initWithNumberOfSides:4 minimumNumberOfSides:3 maximumNumberOfSides:7];
PolygonShape *p2 = [[PolygonShape alloc] initWithNumberOfSides:6 minimumNumberOfSides:5 maximumNumberOfSides:9];
PolygonShape *p3 = [[PolygonShape alloc] initWithNumberOfSides:12 minimumNumberOfSides:9 maximumNumberOfSides:12];
[array addObject:p1];
[array addObject:p2];
[array addObject:p3];
// Log the descriptions
for (id shape in array) {
NSLog(#"%#", shape);
}
// Test the constraints
for (PolygonShape *shape in array) {
[shape setNumberOfSides:10];
}
[p1 release];
[p2 release];
[p3 release];
}
This is the dealloc():
- (void) dealloc {
NSLog(#"Deallocated!!!");
[super dealloc];
}
And this are the results:
2009-11-19 06:58:17.030 Assignment 1B[5441:a0f] --------------------
2009-11-19 06:58:17.030 Assignment 1B[5441:a0f] PRINT POLYGON INFO
2009-11-19 06:58:17.031 Assignment 1B[5441:a0f] --------------------
2009-11-19 06:58:17.031 Assignment 1B[5441:a0f] init: Retain count of 1.
2009-11-19 06:58:17.032 Assignment 1B[5441:a0f] init: Retain count of 1.
2009-11-19 06:58:17.032 Assignment 1B[5441:a0f] init: Retain count of 1.
2009-11-19 06:58:17.033 Assignment 1B[5441:a0f] Hello I am a 4-sided polygon (aka a Square) with angles of 90 degrees (1.570796 radians).
2009-11-19 06:58:17.033 Assignment 1B[5441:a0f] Hello I am a 6-sided polygon (aka a Hexagon) with angles of 120 degrees (2.094395 radians).
2009-11-19 06:58:17.034 Assignment 1B[5441:a0f] Hello I am a 12-sided polygon (aka a Dodecagon) with angles of 150 degrees (2.617994 radians).
2009-11-19 06:58:17.034 Assignment 1B[5441:a0f] Invalid number of sides: 10 is greater than the maximum of 7 allowed
2009-11-19 06:58:17.035 Assignment 1B[5441:a0f] Invalid number of sides: 10 is greater than the maximum of 9 allowed
As you can see, it is not printing the 'Deallocated!!!" message:
Can anyone tell me what I am doing wrong please?
Thanks in advance
The array retains the objects it contains. The objects can only be deallocated once they are removed from the array, or the array is released.
You're leaking your array. You create it with alloc/init, but never release it. Either release it, or create it with [NSMutableArray array].
Also, the dealloc method is called on objects when they're released. However, it looks like you're using a function (not a method of an object) to do that stuff. How is the rest of your code set up?
Note that if you're application terminates, the dealloc method might not be called. The application's memory is cleared anyways and OSX might decide it's faster to just terminate the app and clear the memory without going through all deallocs.
This is documented in the NSObject reference.

Retain Count & Copy In Setter?

This is a followup question from a previous question, which is hopefully a little clearer. I am just curious how the code presented below is working, specifically is the variable myString getting released. It does not look like it is from the output?
CODE
// IMPLEMENT
#implementation CelestialBody
- (void)setName:(NSString *)newName{
if(name != newName) {
[name release];
name = [newName copy];
}
}
- (void)dealloc{
[name release];
name = nil;
[super dealloc];
}
#end
.
// ------------------------------------------------------------------- **
// MAIN: 30th September 2009
// ------------------------------------------------------------------- **
#import <Foundation/Foundation.h>
#import "CelestialBody.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
CelestialBody *newPlanet = [[CelestialBody alloc] init];
NSString *myString = [[NSString alloc]initWithFormat:#"go home"];
NSLog(#"RetainCount_1: %d",[myString retainCount]);
[newPlanet setName: myString];
NSLog(#"RetainCount_2: Incremented by copy in setName");
// Clean up
NSLog(#"RetainCount_2: %d -Before Release",[myString retainCount]);
[newPlanet release];
[myString release];
[pool drain];
NSLog(#"RetainCount_1: %d -After Release",[myString retainCount]);
return 0;
}
// ------------------------------------------------------------------- **
OUTPUT
Running…
2009-10-01 09:28:50.395 RetainCount_1: 1
2009-10-01 09:28:50.399 RetainCount_2: Incremented by copy in setName
2009-10-01 09:28:50.399 RetainCount_2: 2 -Before Release
2009-10-01 09:28:50.400 RetainCount_1: 1 -After Release
Debugger stopped.
I am currently re-reading the Memeory Management Guide to try and see what I have missed.
many thanks
EDIT
Just added a the release to the dealloc, It looks like that was what I was missing.
- (void)dealloc{
[name release];
name = nil;
[super dealloc];
}
gary
is the variable myString getting released.
[myString release];
All signs point to yes.
It does not look like it is from the output?
NSLog(#"RetainCount_2: %d",[myString retainCount]);
[myString release];
Your NSLog statement's output doesn't reflect the release message because the release message hasn't happened yet.
Also, don't worry about retain counts. They can be very misleading. As long as you follow Cocoa's rules and don't create any ownership cycles (A owns B owns C owns A), you'll rarely have a problem.
This is not an answer to your question, per se, but an explanation of what you're seeing: The last call to retainCount is sent to a deallocated object, which is undefined behavior. The object happens not to have been overwritten yet, so it still kindasorta "works" in the sense that the method dispatch can still see the old data that's there and doesn't realize it's invalid. You will never get back 0 from calling retainCount, because such an object can't exist.
I guess [newName copy] doesn't actually copy the NSString, because NSString is immutable? I never thought about this, but it makes sense to me.
At the end of your program you're releasing newPlanet and myString. My question is, are you releasing the instance variable name in the -dealloc method of CelestialBody? If you're not, then I believe you're leaking memory there.
Create myString: the NSString's retainCount is 1
[newPlanet setName:]: the NSString's retainCount is 2
[newPlanet release]: the NSString's retainCount is 2 ? I guess you're not releasing it in -dealloc.
[myString release]: the NSString's retainCount is 1