views:

131

answers:

2

I'm new to Objective-C and iPhone development and have been using Apress' Beginning iPhone 3 Programming book as my main guide for a few weeks now. In a few cases as part of a viewDidLoad: method, ivars like a breadTypes NSArray are initialized like below, with an intermediate array defined and then ultimately set to the actual array like this:

NSArray *breadArray = [[NSArray alloc] initWithObjects:@"White", @"Whole Weat", @"Rye", @"Sourdough", @"Seven Grain", nil];
self.breadTypes = breadArray;
[breadArray release];

Why is it done this way, instead of simply like this:

self.breadTypes = [[NSArray alloc] initWithObjects:@"White", @"Whole Weat", @"Rye", @"Sourdough", @"Seven Grain", nil];

Both seem to work when I compile and run it. Is the 2nd method above not doing proper memory management? I assume initWithObjects: returns an array with a retain count of 1 and I eventually release breadTypes again in the dealloc: method, so that wraps things up nicely:

- (void)dealloc {
   ...
   [breadTypes release];
   [super dealloc];
}   

The setter is defined via a standard property (and eventual synthesize) directive:

@property (nonatomic, retain) NSArray *breadTypes;
+2  A: 

The second way indeed may lead to memory leak; you explicitly create an array (taking the ownership), but you are not releasing it.

The best way to mix create and assignation is to use the class constructors (See NSArray reference). When invoking class constructor, you do not take ownership of the object, so there is memory leaks:

self.breadTypes = [NSArray arrayWithObjects:@"White", @"Whole Weat", @"Rye", @"Sourdough", @"Seven Grain", nil];
Laurent Etiemble
Eventually, the breadTypes array does get released though in this ViewController's dealloc method. There's a [breadTypes release] call right there. Wouldn't that be enough to properly clean up?I'm guessing arrayWithObjects uses autorelease, and the idea here might have been to have tighter control over when something gets released, due to the memory constraints on an iPhone.
Joost Schuur
In your second way, there is a missing release:1) The array creation gives a retain.2) The assignment gives a retain.3) The controller's dealloc gives a release.What you can do it to call release right after the assignment to have the proper count of release. But it depends on the implementation of the getter.
Laurent Etiemble
Laurent, I think I understand now. See my updated post for the property definition that sets up the setter. So in one line, both arrayWithObjects and the setter method both increment the retain count by 1 each? Then you're right, calling release just once in dealloc doesn't clean things up properly. Thanks!
Joost Schuur
Slight correction: I meant '...both initWithObjects and the setter method both increment...'
Joost Schuur
Yes that's right. As the setter is defined with the retain attribute, that means that the passed value is retained.
Laurent Etiemble
A: 

In your second version, there's a memory leak. breadTypes is never going to be released.

Either use the version Laurent Etiemble suggested:

self.breadTypes = [NSArray arrayWithObjects:@"White", @"Whole Weat", nil];

(Many classes provide such convenience constructors. (That means all +classWithSomething:)

In the case of having no such constructor, you could use the alloc, init, autorelease technique:

self.breadTypes = [[[NSArray alloc] initWithObjects:@"White", nil] autorelease];

Of course this uses a little bit more memory and some CPU time, but for most applications it just doesn't matter and you cannot forget to include [obj release], which is the most common reason for memory leaks.

I believe though, Apple recommends not to use autorelease on the iPhone, but as I said, for most cases this won't matter.

Georg
If you look at Apple programming examples the first version is always used. It has an "educational value" Instantiate, set ivar, release. The other version has, as you show, a chance of introducing leaks.In the example above the consequence is minimal, but if you instantiate many objects in a loop using autorelease, they will "hang around" until the autorelease pool is drained, even though they may not be needed.On a device with limited memory I try to avoid autorelease, it can be quite difficult to foresee how far the run loop goes.Autorelease is mainly for returning values, in my book.
RickiG