Releasing local variables before return? - objective-c

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.

Related

calling retain, retainCount analyzing EXC_BAD_ACCESS with release

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.

Will self retain within block?

Before/After call the block, the retaincount is always 1.
From apple block doc we know that the self should retain. Can anyone know why?
NSLog(#"Before block retain count: %d", [self retainCount]);
void (^block)(void) = ^(void){
UIImage* img = [UIImage imageNamed:#"hometown.png"];
[self setImage:img];
NSLog(#"After block retain count: %d", [self retainCount]);
};
block();
First, retainCount is useless. Don't call it..
Blocks only retain captured objects when the block is copied. Thus, self won't be retained by the block in that example.
OK I did some research, now things became more clear. firstly, I didn't use #property on block1, which means when I set it, nothing is copied, so they are not retained, secondly, if we do a [block copy], the variables will be retained, if we dont copy, the block points to a stack address, copy it to heap to make it safe.
the variable 'array' is a Member variable, so it's not retained and meanwhile the self will be retained, whether you put it in the block or not, if the variable is a local variable, it will be retained. ( this is the thing that Im still confused abt, why the member variable is not retained,instead the self is added one more on retain count??? pls answer me?)
after using the block we could set it to nil self.block = nil; to make variables released, and avoid the retain cycle.
PS. a method to break retain cycle is use __block id weakSelf = self; in the block, so it means __block variables are also not retained.

Objective C Array and Object Release

I have a newbie question regarding when to release the elements of a NSArray. See following pseudo code:
NSMutalbeArray *2DArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int i=0;i<10;i++) {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:5];
for (int j=0;j<5;j++) {
MyObject *obj = [[MyObject alloc] init];
[array addObject:obj];
[obj release];
}
[2DArray addObject:array];
[array release];
}
// use 2DArray to do something
[2DArray release]
My question here is, when I release 2DArray, do I need to explicitly release each of its element (array) first? Also, before I release the "array" object, do I need to release each of its element (MyObject) first?
I am new to Objective C. Please help. thanks.
No, you don't need to tell each object to be released. When you send a release method to an NSArray, it automatically sends a release method to each item inside first.
So in your case, you send [2DArray release]. This automatically sends [array release] to every other array, which sends [obj release] to each object inside each array.
You don't need to release the kept objects. NSArray retains them when you add, and releases them when released. So if you allocate, add to the array, then release, the object in the array will have the retain count of 1. Once the array is freed, the object is released, therefore freed.
When an object is created, it has a retain count of 1. Whenever a object is added to an array, its retain count is increased (in this case to 2). After adding to the array, your code release its hold of the object, dropping its retain count by 1 (to 1 in this case). Then when you release the array, it calls release on everything in it dropping their retain counts by 1 (to 0 in this case). When retain count hits 0 the object is deallocated.
Your code looks correct from a memory management stand point.

Understanding Cocoa Memory

What is the advantage of doing this:
NSArray *array = [[NSArray alloc] initWithObjects:#"Year", "#Capital", ..., nil];
self.hintArray = array;
[array release];
Instead of assigning directly to my class variable like this:
self.hintArray = [[NSArray alloc] initWithObjects:#"Year", "#Capital", ..., nil];
Why do we create a temporary local object then release it instead of just assigning to our class variable?
Others have already pointed out the memory issues, but here is the best way to do it in a single step:
self.hintArray = [NSArray arrayWithObjects:#"Year", "#Capital", ..., nil];
The convenience class method +arrayWithObjects returns an array that has already been autoreleased, so you simply don't need to worry about it any more. Your property accessor will take care of copying or retaining it. (assuming, of course, that your hintArray property is set up as a retain or copy property).
You could, but you have to remember to release it once before moving on. The assignment to self.hintArray (assuming it is a synthesized setter that retains on set) will bump the retainCount:
NSArray *array = [[NSArray alloc] initWithObjects:...]; // retainCount is 1
self.hintArray = array; // retainCount is 2
[array release]; // retainCount is 1
and:
self.hintArray = [[NSArray alloc] initWithObjects:...]; // retainCount is 2:
// one for the alloc
// and one for the assign
[self.hintArray release]; // retainCount is 1
Because in the Objective-C reference counted memory management scheme the creation of the array will increment the reference count and if you do not store the return value in a variable you can send a release message to you will have no way to decrement that count and will introduce a memory leak.

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