views:

347

answers:

4

Many iPhone code samples (from Apple, etc.) include code like this:

- (void)viewDidLoad {
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];

// add the top-most parent view
UIView *contentView = [[UIView alloc] initWithFrame:applicationFrame];
contentView.backgroundColor = [UIColor blackColor];
self.view = contentView;
[contentView release];

levelView = [[LevelView alloc] initWithFrame:applicationFrame viewController:self];
[self.view addSubview:levelView];

calibrationView = [[CalibrationView alloc] initWithFrame:applicationFrame viewController:self];
}

This snippet is from the BubbleLevel sample project.

My question: why is a release message sent to contentView? We retain a reference to contentView in self.view and we clearly want to use it for the lifetime of the app, not just in this method. Won't calling release cause the view to be released?

+8  A: 

No, because self.view = contentView will retain it. And since you must balance retain and release calls, you need to release it to balance the alloc.

Ölbaum
+4  A: 

If you're used to other languages, it might be natural to look at self.view = contentView and think, "Oh, it's assigning an instance variable." But it's not — instance variables can't be assigned with a dot, because objects in Objective-C are always pointers. That is a property accessor. In most cases, it is exactly equivalent to writing [self setView:contentView].

In most cases, that setter method will be implemented something like this:

- (void)setView:(UIView *)view {
    if (view != _view) {
        [_view release]; // where _view is the name of the actual instance variable
        _view = [view retain];
    }
}

In a case like this, it is being retained when you set the property. So you have to release it there or else the initial ownership caused by allocing the object will never be released.

Chuck
+11  A: 

Ölbaum is correct in saying that self.view = contentView; will retain your view, which means it won't be deallocated. However, this behavior won't always be the case:

If you look in documentation (or the header) for UIViewController, you'll see that the view property is declared like this:

@property(nonatomic, retain) UIView *view;

This means that whenever the view property is set (foo.view = anotherView or [foo setView:anotherView];), the other view will be retained. If the view property were declared as:

@property(nonatomic, assign) UIView *view;

Then the view would not be retained. It would do a simple pointer assignation into the instance variable. In this case, you would be correct that a subsequent release to contentView would destroy the view, which means the controller would have a stale pointer, which means your app would probably crash.

In other words, when you use properties, make sure you know if they're retain or assign. (If it doesn't say, then it's assign). Then handle your memory management accordingly.

Dave DeLong
+3  A: 

Do not think of retain/release as simply as being something to balance. What they do is transferring ownership. Understand the idea of ownership, and you understand memory management in Cocoa.

The ownership is set in scopes; the method scope, a object instance scope, or global application scope. Methods owns by having retained local variables. Object instances owns by having a retained instance variable. And the application owns by having a retained global variable.

The current method owns the object if you received it from a method call named alloc, or contains the word new or copy (As in newSprocket or mutableCopy). All objects given to you by other means are not owned by you, for example the arguments to your method or the result form most method calls.

You can access object instances that you do not own, you do it all the time as . You only need to explicitly take ownership with retain if you want the object instance to live past the execution of the current method (Or run-loop if you want to be nit-picky).

This line of code takes ownership of the object instance referenced from the local variable contentView. The method loadView now owns the object.

UIView *contentView = [[UIView alloc] initWithFrame:applicationFrame];

Next up the object instance self takes ownership, because the property is declared as retained. The object instance now have two owners, both the method, and the class.

self.view = contentView;

From here on we never access the view object through the local variable in this method. The method no longer needs it, and thus resigns it ownership. Now the object is only owned by the object instance, and is no longer a problem for this method.

[contentView release];
PeyloW
Great explanation.However, I think it might be a good idea to stress that you *should* release the instance, since you allocated it in the first place. It's not only that you dont need it anymore, it's also that at the end of the method, you lose access to your local variable.
Ölbaum