views:

1179

answers:

4

I'm trying hard to understand when and what I must relase in Cocoa Touch as it doesn't have garbage collection.

This code block is from apples iphone sample PeriodicElements and they release anElement and rawElementArray but not thePath, firstLetter, existingArray and tempArray?

I would have thought that at least tempArray and existingArray should be released.

Could some brainy person please explain to me why?

Thanks :)

- (void)setupElementsArray {
NSDictionary *eachElement;

// create dictionaries that contain the arrays of element data indexed by
// name
self.elementsDictionary = [NSMutableDictionary dictionary];
// physical state
self.statesDictionary = [NSMutableDictionary dictionary];
// unique first characters (for the Name index table)
self.nameIndexesDictionary = [NSMutableDictionary dictionary];

// create empty array entries in the states Dictionary or each physical state
[statesDictionary setObject:[NSMutableArray array] forKey:@"Solid"];
[statesDictionary setObject:[NSMutableArray array] forKey:@"Liquid"];
[statesDictionary setObject:[NSMutableArray array] forKey:@"Gas"];
[statesDictionary setObject:[NSMutableArray array] forKey:@"Artificial"];

// read the element data from the plist
NSString *thePath = [[NSBundle mainBundle]  pathForResource:@"Elements" ofType:@"plist"];
NSArray *rawElementsArray = [[NSArray alloc] initWithContentsOfFile:thePath];

// iterate over the values in the raw elements dictionary
for (eachElement in rawElementsArray)
{
    // create an atomic element instance for each
    AtomicElement *anElement = [[AtomicElement alloc] initWithDictionary:eachElement];

    // store that item in the elements dictionary with the name as the key
    [elementsDictionary setObject:anElement forKey:anElement.name];

    // add that element to the appropriate array in the physical state dictionary 
    [[statesDictionary objectForKey:anElement.state] addObject:anElement];

    // get the element's initial letter
    NSString *firstLetter = [anElement.name substringToIndex:1];
    NSMutableArray *existingArray;

    // if an array already exists in the name index dictionary
    // simply add the element to it, otherwise create an array
    // and add it to the name index dictionary with the letter as the key
    if (existingArray = [nameIndexesDictionary valueForKey:firstLetter]) 
    {
    [existingArray addObject:anElement];
    } else {
        NSMutableArray *tempArray = [NSMutableArray array];
        [nameIndexesDictionary setObject:tempArray forKey:firstLetter];
        [tempArray addObject:anElement];
    }

    // release the element, it is held by the various collections
    [anElement release];

}
// release the raw element data
[rawElementsArray release];



// create the dictionary containing the possible element states
// and presort the states data
self.elementPhysicalStatesArray = [NSArray arrayWithObjects:@"Solid",@"Liquid",@"Gas",@"Artificial",nil];
[self presortElementsByPhysicalState];

// presort the dictionaries now
// this could be done the first time they are requested instead

[self presortElementInitialLetterIndexes];

self.elementsSortedByNumber = [self presortElementsByNumber];
self.elementsSortedBySymbol = [self presortElementsBySymbol];

}

+4  A: 

They create rawElementsArray by sending +alloc to the class, therefore this object is owned by the code in the sample above and must be released. Similarly with anElement. Note that thePath and tempArray are not created by sending +alloc, +new or -copy messages, therefore the calling code is not responsible for the lifetime of those objects. Please have a look at this collection of Cocoa memory management articles:

http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html

Graham Lee
+1  A: 

The reason you don't have to release tempArray is because it's been allocated and then autoreleased right away. Autorelease is a method of scheduling a release call sometime in the future, so that the caller of an API doesn't have to do any explicit releasing of the result.

Matt Dillard has provided a detailed explanation of Objective C's memory management strategy and has explained it much better than I can.

rpetrich
+1  A: 

The convention is that when you create an object using a class method it should have been autoreleased. This means that at the end of the run loop when the autorelease pool is flushed these objects will be released. However, if you create something using +alloc] -init] or -copy, -mutableCopy or +new (which is the same as +alloc] -init]) then it will not have been autoreleased.

For example:

NSArray *array1 = [NSArray arrayWithObject:@"foo"];
NSArray *array2 = [[NSArray alloc] initWithObject:@"foo"];

Array1 will be autoreleased and you don't need to worry about it. Array2 will need to be manually released. Or alternatively you could do:

NSArray *array2 = [[[NSArray alloc] initWithObject:@"foo"] autorelease];

Which is pretty much what +arrayWithObject: does.

Of course this leads to an important consideration with the lifetime of instance variables. If you create the instance variable as with array2 then it will be fine as it has a retain count of 1. However, array1 will need to be retained otherwise it will be autoreleased at the end of the runloop, giving it a retain count of 0 and so it will be freed and you will be left with a dangling pointer.

Martin Pilkington
"Which is pretty much what..." Can you expound on that? Also, can you define "runloop"? (Btw, indent code with 4 spaces to format it properly.) Thanks for this helpful answer!
dreeves
A: 

Thanks guys.

Your explanations have made it much clearer now.

Cheers