views:

523

answers:

3

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.

A: 

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?

Dimitri
+3  A: 

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];
}
e.James
I think this might be it. foo will be marked as leaked if a reference to bar is lost without first releasing it.
Daniel Dickison
His comment indicates that bar is, in fact, released. Oh well!
e.James
+2  A: 

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..

craig
I think this is it. Instruments probably flags the fact that bar is allocated within this method's scope, but not released there.
e.James
Yea, I've had issues similar to this one before. Instruments definitely helps in certain situations, but it will never replace sitting down and looking at the code by hand.
craig
Amen. I don't think anything will replace looking at code by hand for a long time : )
e.James
Ugh, so if it isn't released/freed in the same method, does that imply that my code is probably wrong and should be re-designed?
Coocoo4Cocoa
No! It means that Instruments isn't actually smart enough to read through your code to see that you're in fact doing it properly.
craig
No, Instruments doesn’t work like that; it doesn’t look at your code at all. If Instruments is reporting a leak, it means that nothing in your program is pointing at the object. The most likely explanation is a retain imbalance for bar (or its owner).
Ahruman