I fear I am being stupid.
I've spent about three hours tracking down a memory leak that's been destroying my sanity and after commenting out half my app I have come to the following conclusion.
Given the following in the right places.
NSString *blah;
@property (nonatomic, retain) NSString *blah;
@synthesize blah;
-(id)initWithBlah:(NSString*)b {
self.blah = b; //this leaks
blah = b; //this does not
}
I am not particularly experienced with objectice c, I understand that outside of the class if I were calling object.blah = b; I would be transparently calling a setter function which would retain the b. Inside the function I'm presuming by setting it with self.blah = b I'm double retaining for some reason?
Could someone explain to me why that's the case or if not what I might be doing wrong?
Cheers
blah = b
Modifies the class instance variable directly.
self.blah = b
will invoke the accessors if present - thus retaining, releasing resources as needed.
You should add [blah release];
to your dealloc method to free the resource when your class is released.
You are mistaken. The blah = b;
assignment makes little sense because after your code returns from initWithBlah:
you can no longer rely on the string being around. Whenever you access it next, it has very probably already been deallocated.
The self.blah = b;
assignment is correct because it calls the setter and thereby you take ownership of the string. Of course, you have to release blah
in your -dealloc
, too, to prevent memory leaks.
If you give only blah, it does not allocate for the string , if you give self.blah, it then tries to initiate the self and allocates for the self class and try to allocate for the variable which you trying to access , so you have to release it or make the blah=nil in the dealloc method.
i.e
- (void)dealloc
{
self.blah = nil;
or
[self.blah release];
}
Using the accessor is fine, if it leaks then something else is wrong.
In particular your initializer has to be called correctly, i.e. following the Cocoa Memory Management guidelines that parameters are not owned implicitly by the callee.
So the following would be fine as it passes an autoreleased string in:
YourObj *obj = [[YourObj alloc] initWithBlah:[NSString stringWithString:@"blah"]];
While the following leaks due to passing in a retained string:
YourObj *obj = [[YourObj alloc] initWithBlah:[[NSString alloc] initWithString:@"blah"]];
Another thing you have to be aware of is that declared properties do not automatically take care of the clean-up, so be sure to handle that in -dealloc
:
- (void)dealloc {
self.blah = nil;
// ...
}