objective-ciphoneuiviewcontrollernibios

Releasing IBOutlets in UIViewController’s viewDidUnload?


I’m a bit confused as to what happens exactly in Nib-based UIViewControllers. When generating a UIViewController subclass, the template contains a very specific comment in the viewDidUnload method:

// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;

Which subviews does this apply to?

If I use the view controller like this:

MyViewController *controller = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];

In this case I suppose it doesn’t matter much whether instance variables that hold references to subview are released in viewDidUnload or dealloc since dealloc should get called as soon as the view controller is popped off the stack so I might as well do as Apple says and release instance variables on viewDidUnload instead of dealloc.

But suppose I used MyViewController as an instance variable that may be pushed multiple times:

if(self.myViewController == nil) {
    self.myViewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
}
// Change some state which should be reflected in the view
self.myViewController.someProperty = someValue;
[self.navigationController pushViewController:self.myViewController animated:YES];

What happens in MyViewController if I release an IBOutlet in viewDidUnload? Can I count on having a new reference to it on the next viewDidLoad?

In other words: what happens to the view itself after viewDidUnload? Is it released and re-loaded from the Nib if the controller is pushed again? Or does the view remain in memory? And if so, do the outlets get re-set before viewDidLoad?

If either the view remains in memory and the outlets are re-set before viewDidLoad (or is reloaded each time the controller is pushed), I suppose it would be correct to release the outlets in viewDidUnload (even though in the first case it does not matter). But otherwise (specifically if the view remains in memory and the outlets are NOT re-set), releasing the subviews in viewDidUnload is wrong for the use case I presented, am I correct?


Solution

  • From the UIViewController documentation, Memory Management section:

    When a low-memory warning occurs, the UIViewController class purges its views if it knows it can reload or recreate them again later. If this happens, it also calls the viewDidUnload method to give your code a chance to relinquish ownership of any objects that are associated with your view hierarchy, including objects loaded with the nib file, objects created in your viewDidLoad method, and objects created lazily at runtime and added to the view hierarchy. Typically, if your view controller contains outlets (properties or raw variables that contain the IBOutlet keyword), you should use the viewDidUnload method to relinquish ownership of those outlets or any other view-related data that you no longer need.

    So not only can you release your outlets in viewDidUnload, this is the preferred and recommended way.

    And yes, you can count on having your outlets point to valid objects when viewDidLoad is called. Again from the UIViewController documentation, on viewDidLoad:

    This method is called after the view controller has loaded its associated views into memory.