iPhone View Controller Memory Management in a Nutshell (or, What Happens Exactly after didReceiveMemoryWarning?)

When your iPhone or iPod Touch is running low on memory, all your controllers receive didReceiveMemoryWarning. It's like an NSNotification.

In essense, the default implementation (which you are supposed to call by [super didReceiveMemoryWarning]) of didReceiveMemoryWarning releases the view object in your controller if it's not displayed. Any view that is not on top of the navigation hierarchy or in the current chosen tab, for example, is considered not displayed.

Because view is a (readwrite and retain) property, the default implementation zaps your view by assigning that property to nil. In a simplified world, that's the end of story.

But of course we know life is not that simple. Especially if your controller is loaded by an xib.

Problem is, IBOutlets are associated to your controller (see Apple doc for explanation). All IBOutlets, when first instantiated by xib loader, has now a retain count of 1. They are retained for you by default. Usually those IBOutlets are also owned by your view, so by the time you're accessing them, usually they already have a retain count of 2.

So there are two things you have to do:

  1. Release your own IBOutlets when your view is zapped
  2. Zap those IBOutlets (release them and set them to nil) when your controller is dealloc'ed

As of iPhone SDK 2.2.1, Apple does not have a way to let do you 1. Please refer to Craig Hockenberry's article to override -[UIViewController setView:] so that you know when your view is zapped. Things will change for the better in the future, but you can start rolling your own solution.

(Side note: Although if you now follow Apple's newest coding guideline, i.e. making all your IBOutlets readwrite retain properties, those old objects will be released when your controller reloads the view object again—those IBOutlet connections will be re-established and thus old objects will be zapped. But, if you have fat objects like calendar picker or wheel picker, it's best if you release them just now.)

Also remember, when you release your IBOutlets in dealloc, you also zip their pointers to nil. This is because when you finally call [super dealloc], and your view object is zapped, and so Step 1. will be executed, and if your IBOutlets are already released but you still keep their pointer values, your app will crash. This is also exactly what the above-mentioned Apple doc recommends.

Just remember that on iPhone OS, lazy loading is the guiding principle, and always clean up the object and assign the pointer to nil will make life easier for you.