Upcoming and OnDemand Webinars View full list

Real iPhone Crap 1: UINibLoading uses Key-Value Coding

Aaron Hillegass

Last week, my colleague Joe Conway wrote a posting suggesting that dot-notation was not a great addition to the Objective-C language and that he felt that programmers should not use it.  There was outrage. I, myself, was shocked that people cared at all.  After all, there are some examples of truly stupid stuff that Apple has done that are being misused in genuinely dangerous ways by the iPhone developer community.  These are worthy of discussion.  So, I’m doing a multi-part feature that I will call “Real iPhone Crap,” and this is the first installment.


On the desktop, when a nib file is loaded, outlets are set in a sensible way: to set an outlet called foo, the nib loader looks for an accessor called setFoo:. If it is unable to find the accessor,  the nib loader sets the variable foo directly.  This sounds like key-value coding, right?  It isn’t.  The important difference is that nib loading treats foo as a weak reference; the object it points to is not retained.

Thus, if you create a subclass of NSViewController that has a dozen outlets to subviews, only the top-level view is retained.  So, when the view controller is deallocated, it releases the top-level view and all the subviews are automatically deallocated.  Tidy!

On the phone, however, the nib loader uses key-value coding to set the outlets; By default, outlets are treated as strong references.  If you don’t have an accessor for your outlet, the view it refers to is retained.

Thus, if you create a subclass of UIViewController that has a dozen outlets to subviews, all of the subviews are retained.  When the view controller is deallocated,  the top-level view is released automatically, but you (in dealloc) still need to explicitly release all the subviews that you have outlets to.

But it is even worse than that on the phone because that top-level view is, by default, released if it is offscreen when a memory warning arrives.  So, the view may be destroyed and recreated many times during the life of the view controller.  You must also explicitly release the retained subviews in viewDidUnload. (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.)

The use of key-value coding in nib loading is idiotic.  (Way more idiotic than dot-notation.)  There was a way that worked fine for 15 years, and Apple loused it up.  This stupidity and the developer community’s misunderstanding of it is the largest source of memory leaks on the phone.

There are two possible ways to fix this memory leak:

  • Release all your outlets in dealloc and viewDidUnload (Make sure you set them to nil in viewDidUnload.)

  • Make your outlets weak references

I haven’t seen many programmers using the second option, so let me be more explicit.  If you have an outlet called crapButton, declare it thusly:

@interface CrapViewController : UIViewController
{
    IBOutlet UIButton *crapButton;
}
@property (nonatomic, assign) IBOutlet UIButton *crapButton;

Then, in CrapViewController.m:

@synthesize crapButton;

Now that button is not being retained. (You will need to be sure that view is loaded before you can send any messages to this button.)

One of the nice things about not being an Apple employee anymore, is that I can tell my students honestly: “This is crap, and this is the way we work around it.” I hope this series proves useful to those who have not been able to attend our Beginning iPhone Bootcamp (for beginning programmers) or our iPhone Bootcamp (for experienced programmers).

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project