2013-04-20

TL;DR: Don’t declare IBOutlets using @property (weak) anymore.
Just use private ivars: ARC will take care of the rest.


##

IBOutlets are, as the name implies, what links your Objective-C code to your nib files (“IB” stands for Interface Builder1).

It’s not a language keyword (IBOutlet is actually defined as “nothing” for the compiler) Interface Builder uses them to find the connections that can be made from your UI objects. Then at runtime, the Nib-Loading mechanism will assign the deserialized view to the corresponding outlet in your code.

Pretty simple actually. However, this ancient feature has evolved over time, mostly on (1.) how they are declared in code and (2.) their memory management.

A long time ago, in the old-style Cocoa world, outlets were simply declared as instance variables:

@interface MyController : NSObject {
    IBOutlet id outlet;
}
 @end
@implementation MyController

- (void) dealloc
{
    [outlet release];
    outlet = nil;
    [super dealloc];
}

@end

Nothing fancy here: the Nib-Loading simply alloc-initialized new objects and assigned them to the outlets, without any additional retain or release. Of course, they had to be released in dealloc.

Objective-C 2.0 introduced named properties, which could be used to declare IBOutlets using @property instead of accessing ivars directly:

@interface MyController : NSObject
@property (nonatomic,retain) IBOutlet NSView * anOutlet;
@end
@implementation MyController
@synthesize anOutlet=_anOutlet;

- (void) dealloc
{
    [anOutlet release];
    anOutlet = nil;
    [super dealloc];
}

@end

We had a slightly more precise semantic of the memory management, at the cost of some boilerplate in the @property declaration and its @synthesize. Of course, we could get rid of the nonatomic qualifier: we don’t need atomic properties, but performance is rarely an issue when loading a nib file.

With the introduction of iOS, everything changed: Views could be unloaded without their View Controller being deleted. Enter viewDidUnload:

@interface MyController : UIViewController
@end
@interface MyController()
@property (retain) IBOutlet UIView * anOutlet;
@end

@implementation MyController
@synthesize anOutlet=_anOutlet;

- (void) viewDidUnload
{
    self.anOutlet = nil;
    [super viewDidUnload];
}

- (void) dealloc
{
    [anOutlet release];
    anOutlet = nil;
    [super dealloc];
}

@end

For a single outlet, we had to write five statements, and an additional method just for IBOutlet management. Now that’s boilerplate!

(At least, once auto-nil weak properties became available, we were able to ignore a lot of it, at least for outlets connected to subviews.)

Thankfully, ARC came and allowed us to completely remove the dealloc method. In the meantime, properties were upgraded with auto-synthesize, and became strong by default:

@interface MyController : UIViewController
@end
@interface MyController()
@property IBOutlet UIView * anOutlet;
@end

@implementation MyController

- (void) viewDidUnload
{
    self.anOutlet = nil;
    [super viewDidUnload];
}

@end

The final blow came with iOS 6, which deprecated viewDidUnload. Since ARC makes ivars strong by default, we can now simply write this:

@interface MyController : UIViewController
@end
@implementation MyController {
    IBOutlet UIView * _anOutlet;
}

...

@end

Let’s discuss it a little bit:

  • With ARC, and because viewDidUnload is never called, there’s little point in using weak outlets, even if it is still what the documentation recommends2. A strong ivar will retain it once more, and release it in the dealloc, but the lifespan of the object will just be the same.
  • An outlet is (almost always) an implementation detail of its ViewController or custom View. It’s not an API, and should not appear in the .h. Private ivars are the place for this.
  • Finally, @properties should be reserved for actual state properties, like the model object being presented, the display mode, etc. An outlet is certainly not a “property” with this definition, so again, private ivars make sense.

To sum up, we can now declare IBOutlets as ivars and forget about it. It’s actually the shortest and the most logical way to do it.

##

Comments are welcome, via nico@bou.io or on twitter.

##

This post is adapted from a Small Talk (in french) I gave at Cocoaheads Paris in april 2013.

  1. I still refer to the UI Editor in Xcode 4 as “Interface Builder”. 

  2. The doc reads: “Outlets should generally be weak, except for those to top-level objects.”. I respectfully disagree.