views:

3415

answers:

3

The function I'm looking at:

-(void)viewDidLoad {
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *plistPath = [bundle pathForResource:@"statedictionary" ofType:@"plist"];

    NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
    self.statesZips = dictionary;
    [dictionary release];

    NSArray *components = [self.stateZips allKeys];
    NSArray *sorted = [components sortedArrayUsingSelector:@selector(compare:)];
    self.States = sorted;

    NSString *selectedState = [self.states objectAtIndex:0];
    NSArray *array = [stateZips objectForKey: selectedState];
    self.zips = array;  
}

Why is an NSDictionary allocated, then assigned to a pointer called *dictionary, and then assigned to the instance variable stateZips? Why not allocate it and assign it directly to the instance variable and save memory of creating and releasing another NSDictionary? The same methodology is always followed, including later in this function with the NSArray...

NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];

Also, this sorting puts the keys from a hash table (dictionary) in alphabetical order. I'm not sure I understand this line:

NSArray *sorted = [components sortedArrayUsingSelector:@selector(compare:)];
+2  A: 

The statesZips property is probably retained, that's the reasoning.

When the NSDictionary is first allocated, its retain count is 1. When it's assigned to statesZips, the retain count becomes 2. When it's released, the retain count drops to 1, which is usually the desired outcome.

Note that the code below would have produced (almost) the same result:

self.statesZips = [NSDictionary dictionaryWithContentsOfFile:plistPath];

because dictionaryWithContentsOfFile returns an autoreleased object.

As a convention, class methods like [NSDictionary dictionary] return autoreleased objects (which automatically get released after some time), while the usual alloc-init method (as in [[NSDictionary alloc] init]) return retained objects.

I suggest you read the Memory Management Programming Guide for Cocoa for further information.

EDIT: I must have missed the last part of your question when I first read it, but Barry has already answered that part.

Can Berk Güder
+1  A: 

This code uses reference-counted memory management (not the automatic garbage collection memory management available in Objective-C 2.0 on OS X). When any object (in this case, the NSDictionary and the NSArray) are alloc'd, the caller is responsible for calling -release on that instance. Failing to call release causes a memory leak. The code could have been written as

self.statesZips = [[[NSDictionary alloc] initWithContentsOfFile:plistPath] autorelease];

but at the expense of less explicit memory management (relying on NSAutoreleasePool to release the alloc'd instance at the end of the event loop iteration.

the call

[components sortedArrayUsingSelector:@selector(compare:)];

returns an array of whose elements come from components but according to the return value of calling [elem1 compare:elem2] to compare two array elements.

Barry Wark
+5  A: 

No one seems to have addressed the fact that the line

self.statesZips = dictionary;

is not directly an instance variable assignment. stateZips is a property, and so that line of code calls the setStateZips: method. That method retains or copies the dictionary, so unless the viewDidLoad method intends to use it again for some purpose, it's not needed any longer. That makes it OK to release it.

The previous line:

[[NSDictionary alloc] initWithContentsOfFile:plistPath];

allocates an object. That makes it your responsibility to release it once you don't need it any more. After assigning it to the statesZips property, it's no longer needed, so it's released and you shouldn't use dictionary any more. You'll notice that later code only refers to self.stateZips, not dictionary.

In the case of the NSArray later in the method, viewDidLoad does not allocate the object, so that method is not responsible for calling release on it. The rule of thumb is that if you alloc it, you're responsible for making sure it gets released. Otherwise, it's not your problem.

Sorting the array uses the sortedArrayUsingSelector: method. A selector identifies a method in Objective-C. And the @selector is the literal syntax for selectors (kind of like how @"" is the literal syntax for NSString objects). So, what that code says, is "give me an array where the objects in components are sorted, and use the compare: method to compare each object when you do the sort. When it sorts the array, it will call compare: on the objects in the array to determine how to put them in order.

Alex
I actually think that the retain/assign semantics of the statesZips property is irrelevant. The code allocates an object and so needs to release it, *regardless* of the semantics of statesZips, unless it's running in a garbage-collected environment.
Barry Wark