views:

154

answers:

2

I'm following a book on iPhone development and there is a particular pattern I keep seeing in the example code that doesn't make much sense to me. Whenever a property is set they first assign a pointer to the new value for the property, then set the property to the pointer, then release the pointer. Example:

Interface:

@interface DoubleComponentPickerViewController : UIViewController {
    NSArray *breadTypes;
}

@property(nonatomic, retain) NSArray *breadTypes;

@end

Class method:

- (void)viewDidLoad {
    NSArray *breadArray = [[NSArray alloc] initWithObjects:@"White", @"Whole Wheat", @"Rye", @"Sourdough", @"Seven Grain", nil];
    self.breadTypes = breadArray;
    [breadArray release];
}

Is there any reason to do this instead of just doing the following?

- (void)viewDidLoad {
    self.breadTypes = [[NSArray alloc] initWithObjects:@"White", @"Whole Wheat", @"Rye", @"Sourdough", @"Seven Grain", nil];
}

Thanks for the light that will no doubt be shed :)

+3  A: 

Yes. Those methods are alloc'ing the variables so they must be released. The fact that the property has a retain attribute means that when you say @synthesize breadTypes; the compiler is actually generating a setBreadTypes that properly releases the current breadType member and retains the new one. Thus your function must not retain the variable it alloc'ed.

You could, however write:

- (void)viewDidLoad {
    self.breadTypes = [[[NSArray alloc] initWithObjects:@"White",
                              @"Whole Wheat", @"Rye", @"Sourdough",
                              @"Seven Grain", nil] 
                        autorelease];
}

You'll want to brush up on Cocoa Memory Management

nall
Thanks for the great answer. I will read the supplied link.
Kenny
+8  A: 

Let me try and explain it in a different way.

A method that has alloc, copy or new in its name will allocate memory for an object, and gives ownership of that object to the caller, and it is the caller's responsibility to release that memory.

In your viewDidLoad method, you call a method that gives you ownership of an object. It is your method's responsibility to release it. However, before you do that, you want to do something with it - after all, that's why you alloc'ed it, not to just release it, but to do something useful with it.

Regardless of what it is that you want to do with it, you have to release it (or autorelease it*). In this case your use of the object is to pass it to self.breadTypes. self.breadTypes may not look like a method, but it is (it is a setter). You pass it breadArray. It does what it needs to with it. It might retain it for use later, or it might copy some info out of it, or make a copy of the entire thing. Your viewDidLoad doesn't really care. It assumes that self.breadTypes does what it needs to and when it returns, it doesn't care what you do with breadArray.

And what you do with it, is what you have to do with anything that you own - release (or autorelease* it).

That's why you have to use the temp variable, breadArray. You can't quite release the results from alloc on the same line, since the object would get released before self.breadTypes can have at it:

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

Thus you are forced to assign to a temp variable, pass it to self.breadTypes, and then release the object that is saved in breadArray.

Now, you could try to do it this way so you don't use a temp variable:

- (void)viewDidLoad {
    self.breadTypes = [[NSArray alloc] initWithObjects:@"White", @..., nil];
    [self.breadTypes release];
}

but that is not very efficient since you are calling yet another method (self.breadTypes as a getter) that you didn't really need to if you have just stored the value in a temp variable.

*Now, as a responder said, you could use autorelease for an alternative version:

- (void)viewDidLoad {
    self.breadTypes = [[[NSArray alloc] initWithObjects:@"White", ..., nil] 
                        autorelease];
}

Apple urges us to think twice about whether we want to use autorelease vs. release. Autorelease may not be the best choice for every situation. I personally like to clean up after myself as soon as I possibly can, and not use autorelease needlessly. Autoreleased objects get released at the end of the execution of the run loop, for example soon after viewDidLoad returns. You should read up a bit more about autorelease (and memory management on the iPhone which is slightly different than MacOS X Cocoa), as I am oversimplifying it all.

BTW: If you retain an object, you are assuming ownership of it, and you will have the same responsibility again, to release it after you are done with it.

mahboudz
Thanks for taking the time to write all that up. It was very helpful.
Kenny