Aaron Hillegass recommends making your UIKit IBOulets weak references. Don't.
Hillegass's main argument is that Apple messed it up when they changed the way IBOutlets were associated when an nib is deserialized. On desktop, only the topmost view is retained, all other IBOutlets are weak. On iPhone, every IBOutlet is retained in the beginning. You have to remember to release them when (1) your top-level view is released, (2) when your controller receives -dealloc
, of course.
While it is true that Apple did a terrible documentation work in 2.2.1, resulting in that us developers have to go through this clean up hack, and the -viewDidUnload:
method that is added to SDK 3.0 is still not well documented (for example, Apple doesn't tell you when your controller receives -dealloc
, no -viewDidUnload:
was called before that), but telling people to make IBOutlets weak simply because it's "idiotic" is dangerous.
In terms of memory management, an iPhone view controller differs from its Mac counterpart in two most important aspects:
The view object is loaded on demand, and unloaded without prior notice (on 3.0, you only get that notice, -viewDidUnload:
, after it's a fait accompli.
Your controller will receive -didReceiveMemoryWarning
, but the order in which it's received is indeterministic1. It can happen any time, in any order, at any frequency, depending on system load and demand.
So wait until your app goes multithreaded, or until when your apps needs to update UI states when their parent view is gone, or simply when you have controllers in a tab bar.
Yes, it's always a good practice to zap all your IBOutlets to nil in all of the memory management methods. The problem is you will more easily forget doing that if you're using a weak reference. Until when your app starts to crash in the middle of nowhere.
If you observe Apple's iPhone coding style, you'll find Apple advises developers to make all their IBOutlets properties. There's a reason for that. For, if you do, and you just forget zapping them2 in -viewDidUnload:
3, the worst that can happen to you is you have unreleased IBOutlets until the view is loaded again. Even Hillegass acknowledges this:
(OK, I just reread this and realized that the reader might get the idea that the retained views are leaking every time the view is unloaded. It is not quite that bad. A retained view will linger until the view is reloaded. At that point, it will be released as a side-effect of the setValue:forKey: that gets called when the outlet is set again.)
So in fact the biggest source of iPhone app memory leak is not caused by the reasons that Hillegass disparaged. It's often because developers forget *they're responsible for the objects created in -viewDidLoad:
and forget doing that in both -viewDidUnload:
and -dealloc
(-didReceiveMemoryWarning
is more suitable for releasing and nullifying data model objects or cache objects). Instead, Hillegass suggested a "workaround" that does not help—it actually hurts. The memory leaks caused by your oversight will actually remind you to clean up. With weak references, there's no such problem—until it resurfaces as other bugs that are even harder to tackle.
And Craig Hockenberry actually already explained why you can't leave weak references back in 2.x days4.
I don't take side on the recent dot syntax "debate" (summary of which can be found here). I can understand the case against it on pedagogical reasons—but that's pretty much where my sympathy stays. Hillegass's new advice, on the other hand, is both counterproductive (it doesn't identify the rationale and iPhone's design needs) and outright dangerous.
SDK on iPhone does not work like they do on Mac for a reason. Hillegass's advice totally missed on that.
Your view controller stops receiving -didReceiveMemoryWarning
only after you call [super dealloc]
. It's entirely possible for your view controller to receive that message between your dealloc code and [super dealloc]
! ↩
One fine detail though: Using properties to nullify them is discouraged, although there's usually no harm, in -dealloc
. Even Apple sample code does that—though not that it means it's the absolutely right way to do. We use a macro internally. ↩
Here I'll skip the details if you want everything works on both iPhone OS 2.2.x and 3.x—which some of your clients may still require you to do. Just remember -viewDidUnload:
is not called before your controller receives -dealloc
on OS 3.x. ↩
I have also written something on this: "iPhone View Controller Memory Management in a Nutshell". ↩