views:

858

answers:

2

I have an Objective-C class that looks something like:

@interface myClass : NSObject
{
    NSMutableDictionary *aDict;
}

Its setter method looks like:

- (void) setADict: (NSMutableDictionary *) newDict
{
    [aDict release];
    [newDict retain];
    aDict = newDict;
}

I've created an instance of the object, put data into aDict, and now want to get rid of the object, cleaning up its memory in the process. Two questions:

1) Should I release aDict in myClass's dealloc method, as shown?

[aDict release];

=============================================================================

2) I've nested several children dictionaries into aDict, represented by keyA and keyB, like so:

// aDict:
{
    keyA =     {
        keyA1 = "Hello world";
        keyA2 = 1234;
    };
    keyB =     {
        keyB1 = Earth;
        keyB2 = 25000;
    };
}

Will a [aDict release] message "propagate down the chain" and cleanup all children dictionaries inside of aDict, including their key/value objects? Or, do I have to do something else to get rid of them?

+3  A: 

Yes.

Collection objects retain the things they're collecting, so when the collection is deallocated, it balances the retain with a release.

Then if one of the dictionaries it was holding onto is deallocated, it will also release its key/value pairs, since the dictionary is a collection, and collection objects retain the things they're collecting.

The same holds true for arrays and sets.

However, you have a bug in your code. You have:

- (void) setADict: (NSMutableDictionary *) newDict
{
    [aDict release];
    [newDict retain];
    aDict = newDict;
}

Consider the case when newDict is the same object as aDict. Then you release the dictionary, it is deallocated, and then you attempt to retain it, but whoops! It's no longer there.

There are three alternatives to fixing this. Here they are:

    - (void) setADict: (NSMutableDictionary *) newDict
    {
        [aDict autorelease];
        [newDict retain];
        aDict = newDict;
    }

    - (void) setADict: (NSMutableDictionary *) newDict
    {
      if (aDict != newDict) {
        [aDict release];
        [newDict retain];
        aDict = newDict;
      }
    }

    - (void) setADict: (NSMutableDictionary *) newDict
    {
        [newDict retain];
        [aDict release];
        aDict = newDict;
    }

I'd say the third one is simplest, the second one is fastest over time, and the first is meh. The first will clutter up your autorelease pools (which may or may not lead to memory bloat). The second and third are equivalent in terms of safety and property memory management. However, the second will save you unnecessary method calls if you happen to be setting the same dictionary over itself.

Dave DeLong
Wow, thanks for the answer, and thanks so much for catching the bug! :) I'm looking at "Cocoa Programming for Mac OX X" by Hillegass and he has the exact same three mem-management idioms listed in there. Stupid me reversed the retain/release ordering, ha ha. I won't forget it now, promise!
Dave Gallagher
+2  A: 

NSMutableDictionary will release all objects (values) on release.

NSMutableDictionary copies key and send retain to value for each key/value pair that you added. Cocoa practice is that when something retains reference then it is responsible for releasing it.

See NSMutableDictionary reference

Methods that add entries to dictionaries—whether as part of initialization (for all dictionaries) or during modification (for mutable dictionaries)—copy each key argument (keys must conform to the NSCopying protocol) and add the copies to the dictionary. Each corresponding value object receives a retain message to ensure that it won’t be deallocated before the dictionary is through with it.

stefanB