I have some code that looks like the following:
NSMutableArray *bar = [[NSMutableArray alloc] initWithCapacity:0];
NSMutableDictionary *foo = [[NSMutableDictionary alloc] initWithCapacity:0];
[foo setObject:[NSNull null] forKey:#"yay"];
[bar addObject:foo];
[foo release];
Instruments is showing that foo is leaking. I understand why that is happening. Foo's retain count when alloc'd is 1. Then when bar addObject's foo, the retain count goes to 2. Later when I release foo, it goes down back to 1. Still a leak. However, later on in my code, (in a separate method, which is why I think this might be shown as a leak)
[bar removeAllObjects];
Why is foo shown as leaking if I do removeAllObjects later on?
** NOTE **
I didn't include it in my original post, but bar is indeed being released in the classes dealloc method.
I think (and I think you hinted at this possibility as well) that Instruments is marking it as a potential leak, because it hasn't looked ahead far enough to see that bar will be responsible for removing/releasing all its objects in said separate method..
Given what you show, it is bar that never gets released. Calling [bar removeAllObjects] only removes the objects it contains. Instead, you should call [bar release] when you are done with bar. This will automatically release all of the objects that bar holds, plus release the bar object itself.
You state that you understand the memory management concepts, so perhaps you just didn't show bar being released in your example.
edit: I think craig has the right idea in his answer. One way to avoid the warning (maybe) would be to allocate bar in the class init method. I usually find it beneficial to maintain a symmetry between my init and dealloc methods when it comes to member variables, and this would be a good example:
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
bar = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[bar release];
[super dealloc];
}
- (void)YourMethod
{
NSMutableDictionary *foo = [[NSMutableDictionary alloc] initWithCapacity:0];
[foo setObject:[NSNull null] forKey:#"yay"];
[bar addObject:foo];
[foo release];
}
The NSMutableArray should be releasing it when removeAllObjects is called. You shouldn't need to release or add it to an autorelease pool.
From O'Reilly's chapeter on Memory Management:
When you add an object to a collection, it is retained. When you remove an object from a collection, it is released. Releasing a collection object (such as an NSArray) releases all objects stored in it as well.
Perhaps something else is going on in instruments?
Related
I am doing my project in xcode 4.2 (Older Version). For my application, I just set the variables, arrays in dto class for using in entire app lifecycle. so I set with a property like this.
AppDTO(sub class of NSObject)
AppDTO.h
#property(nonatomic,retain)anotherAppDTO *aAppDTO;
#property(nonatomic,retain)NSMutableArray *array1;
#property(nonatomic,retain)NSMutableArray *array2;
#property(nonatomic,retain)NSString *string1,*string2,*string3;
AppDTO.m
- (id)init
{
self = [super init];
if (self) {
self.aAppDTO = [[anotherAppDTO alloc]init];
self.array1 = [[NSMutableArray alloc]init];
self.array2 = [[NSMutableArray alloc]init];
self.string1 = #"Hello";
self.string2= #"Hai";
}
}
-(void)dealloc
{
if(array1 != nil)
{
[array1 release];
array1 = nil;
}
if(array2 != nil)
{
[array2 release];
array2 = nil;
}
[aAppDTO release];
aAppDTO = nil;
[super dealloc];
}
when I analyze my app in Xcode 4.3.2, I get memory warning in self.array1 and self.array2 (Potential leak on object allocated on line….), but when I change self.array1 to array1, warning goes away.
What is the reason for using self. do I need to use self if I set #property(nonatomic,retain) to variables(like array1,array2,string1,string2).
Also in dealloc method, I heard we don't want to use [self.array1 release], instead we can use [array1 release]. Is it Correct?
Do I need to release my string in dealloc method.
Also I am releasing aAppDTO in dealloc method. if I allocate some objects in anotherAppDTO class, will it release automatically when I call [aAppDTO release] method.
Can anyone clarify me.
Many Thanks,
Anish
You get the warning because when you write :
self.array1 = [[NSMutableArray alloc]init];
is the same as :
[self setArray1: [[NSMutableArray alloc]init]];
As you can notice you are not allocating the underlying array1 private variable, but you are calling the setter of the property that since it is declared as retain it retains the object once assigned, this means that when you eventually will assign another object the second time with the setter the first object will remain with a retain count of one until the application will be closed (since you don't have any reference to that object anymore ...) .
Take a look at this great article to understand better Manual Reference Counting in Objective-C .
when i analyze my app in Xcode 4.3.2, i get memory warning in self.array1 and self.array2 (Potential leak on object allocated on line….), but when i change self.array1 to array1, warning goes away.
the analyzer's right. the parameter is retained when set. as well, you should favor direct access in initialization and dealloc. so, you should just write array1 = [[NSMutableArray alloc] init];, and be done.
What is the reason for using self. do i need to use self if i set #property(nonatomic,retain) to variables(like array1,array2,string1,string2).
those go through the accessor methods. if not in initialization or dealloc, you should favor going through the accessor methods because that is the common correct execution path for a fully constructed object.
Also in dealloc method, i heard we don't want to use [self.array1 release], instead we can use [array1 release]. Is it Correct?
correct.
Do i need to release my string in dealloc method.
yes.
Also I am releasing aAppDTO in dealloc method. if i allocate some objects in anotherAppDTO class, will it release automatically when i call [aAppDTO release] method.
when its reference count reaches 0, its dealloc will be called.
I think the others have answered your question.
I do want to draw your attention to Apple's excellent Advance Memory Management Programming Guide: Practical Memory Management, in which they walk through these sorts of scenarios. It's hard to take it all in on the first reading, but it really does cover this stuff. In answer to your question about the use of instance variables versus the accessor methods, I draw your attention to the section labeled to "Don't Use Accessor Methods in Initializer Methods and dealloc".
According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!
sample code:
- (Foo*)createFoo {
Foo *foo = [[Foo alloc] init];
return foo;
}
- (void)someOtherMethod {
Foo *foo;
foo = [self createFoo]; //retain count 1
[foo release]; //retain count = 0 => object gets released?
//repeat
foo = [self createFoo];
[foo release];
}
Question: Do i have to autorelease in createFoo or can i release the returned object in someOtherMethod?
Your code in this instance should be autoreleasing your object as you are handing over ownership to the calling code, you no longer wish to be responsible for it within the method and so you should relinquish your retain on it.
Remember NARC - methods that begin with these keywords are assumed to NOT autorelease...
New, Alloc, Retain, Copy
If your method were named newFoo or copyFoo then your code above would be fine without autoreleasing.
Cocoa memory management is actually quite easy because everybody sticks to a set of rules. You aren't following those rules, so you're going to run into trouble.
Read Basic Memory Management Rules. If you stick to following those rules, you should be fine.
myclass.m
- (id) init {
self = [super init];
if (self != nil) {
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self; // send loc updates to myself
}
return self;
}
- (void)dealloc {
[locationManager release];
[super dealloc];
}
I use instrument to check leak. The leak always point to
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
Why?
I use instrument to check leak. The
leak always point to
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
Why?
Because that is the line of code where the allocation that is leaked occurred. Since you have balanced both implied retains in that code correctly (once here, once in -dealloc -- mmccomb's suggestion of direct assignment without autorelease is good, but won't fix the problem), the leak is elsewhere.
Specifically, the leak will be a retain that isn't balanced by a release. So, somewhere your are retaining the object and not releasing it.
Instruments can be used to show all retain/release events on any given object. Use that and look through the list of events related to your leaked object. There will be one more retain than release. Pair off the retains and releases. Whichever retain is left without a balanced release is the cause.
I wrote an article about using Heapshot analysis in Instruments to detect memory abuse. It includes discussion and screenshots showing the retain/release event inspector and will be applicable.
Try to avoid using the self. setter notation in init methods. When an object is being initialised there is no guarantee that it is in a consistent state. Change your implementation to directly set the ivar as follows...
locationManager = [[CLLocationManager alloc] init];
You no longer need the autorelease call which was previously accounting for the extra retain count brought about by invoking the setter. You do however need to release the object in your dealloc method (as you are already doing).
You can explicitly check the retain count in dealloc like so:
NSLog(#"locationManager retain count: %d", [locationManager retainCount]);
If it is more than 1, check where else you may be retaining it - like if you assign it to another retaining property (e.g. declared with retain keyword). You can add that NSLog call in other locations before and after you do something with locationManager and see where the retain count increases. Sometimes it may not be immediately obvious.
Another possibility: does the dealloc method even get called? Perhaps the whole "myclass" object is not properly released? (Although I suppose in that case you'd see a leak of type "myclass", too).
Given the following property definition:
#property (nonatomic,retain) MyObject* foo;
does the following code cause a memory leak:
self.foo = [[MyObject alloc] init];
?
It looks like the alloc call increments the retain count on the object to 1, then the retain inside the property setter increases it to 1. But since the initial count is never decremented to 0, the object will stick around even when self is released. Is that analysis correct?
If so, it looks like I have two alternatives:
self.foo = [[[MyObject alloc] init] autorelease];
which is not recommended on the iPhone for performance reasons, or:
MyObject* x = [[MyObject alloc] init];
self.foo = x
[x release];
which is a bit cumbersome. Are there other alternatives?
Are there any alternatives?
No.
You are not going to be able write much of an iPhone application without using autorelease and the Cocoa Touch library uses them in many places. Understand what it's doing (adding the pointer to a list for removal on the next frame) and avoid using it in tight loops.
You can use class method on MyObject that does alloc/init/autorelease for you to clean it up.
+ (MyObject *)object {
return [[[MyObject alloc] init] autorelease];
}
self.foo = [MyObject object];
The easiest way to manage a retained property on the iPhone is the following (autorelease is not as bad as you think, at least for most uses):
-(id)init {
if (self = [super init]) {
self.someObject = [[[Object alloc] init] autorelease];
}
return self;
}
-(void)dealloc {
[someObject release];
[super dealloc];
}
The autorelease releases the reference to the floating instance which is assigned to self.object which retains its own reference, leaving you with the one reference you need (someObject). Then when the class is destroyed the only remaining reference is released, destroying the object.
As described in another answer, you can also create one or more "constructor" messages to create and autorelease the objects with optional parameters.
+(Object)object;
+(Object)objectWithCount:(int)count;
+(Object)objectFromFile:(NSString *)path;
One could define these as:
// No need to release o if fails because its already autoreleased
+(Object)objectFromFile:(NSString *)path {
Object *o = [[[Object alloc] init] autorelease];
if (![o loadFromFile:path]) {
return nil;
}
return o;
}
You are right, self.foo = [[MyObject alloc] init]; is leaking memory. Both alternatives are correct and can be used. Regarding the autorelease in such a statement: keep in mind that the object will released by the autorelease pool as soon as the current run loop ends, but it will most probably be retained a lot longer by self, so there is no issue with memory usage spikes here.