There are mixed opinions on whether IBOutlet
properties should be strong or weak.
Apple themselves changed their recommendation with iOS 5.
However their advice right now is bad — or at least poorly reasoned.
The rest of the Cocoa guidelines and my own experience both lead me to the opposite conclusion: declare outlets as strong.
To clarify, I’m talking about outlets from controllers to views — outlets on “File’s Owner”, often a UIViewController
, NSViewController
, or NSWindowController
instance.
(And yes, “owner” does imply “strong”.)
Outlets from views to controllers or other views — for example a table view subclass adding an awesomeSource
— should be weak.
Tedious analysis follows.
Apple’s reasons
Apple documents their recommendations in the Resource Programming Guide: Nib Files:
Outlets should generally be
weak
, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should bestrong
. Outlets that you create should therefore typically beweak
, because:
- Outlets that you create to subviews of a view controller’s view or a window controller’s window, for example, are arbitrary references between objects that do not imply ownership.
- The strong outlets are frequently specified by framework classes (for example,
UIViewController
’s view outlet, orNSWindowController
’s window outlet).@property (weak) IBOutlet MyView *viewContainerSubview; @property (strong) IBOutlet MyOtherClass *topLevelObject;
But there are caveats to using weak:
Outlets should be changed to
strong
when the outlet should be considered to own the referenced object:
- As indicated previously, this is often the case with File’s Owner—top level objects in a nib file are frequently considered to be owned by the File’s Owner.
- You may in some situations need an object from a nib file to exist outside of its original container. For example, you might have an outlet for a view that can be temporarily removed from its initial view hierarchy and must therefore be maintained independently.
To summarize, the documentation says, “use weak when you can, but it doesn’t always work”.
If you do use weak references in the above cases, it will sometimes work (both UIViewController
and NSViewController
actually do retain the top-level objects of a nib, but it’s undocumented), and sometimes fail (removing a view from its superview, if that was the last strong reference).
Oh, and remember to change them from weak
to strong
if the layout of the nib changes, or if you start doing something different with the connected objects.
And remember to change them from strong
back to weak
because… well, why exactly?
Each of the above reasons sounds like, “some objects already have a retaining reference, so they shouldn’t have another”, which is an argument against reference counting in favor of malloc
/free
semantics.
Consider a simple case:
@interface ChooseOne : NSObject
@property (nonatomic, strong) NSArray *options; // @[@"one", @"two", @"three"]
@property (nonatomic, ?) NSString *selectedOption;
@end
The options
property indirectly holds a strong reference to all the options, so by the nib doc’s first reason selectedOption
should be declared weak
.
That could work, but it’s overly fragile:
- If
selectedOption
is set beforeoptions
, the value may or may not be nil. Even worse, the outcome could depend on the compiler’s optimization setting: Everything could look fine in Debug, but fail in Release! - There are values that compare equal but aren’t identical, such that
[choice.options containsObject:opt]
is true, but when stored inselectedOption
would silently be lost.
The sane storage specifier for selectedOption
is at least strong
(copy
is even more appropriate).
The docs also convey the feeling that weak
is an optimization. With my memory-management goggles on, this is how I read the above:
- Outlets that are connected to subviews are already retained by the top-level
view
(orwindow
) outlet, so they shouldn’t bother with that retain/release stuff. - The outlets that do need to do these “heavy-weight” retain/release calls are typically provided by the system.
This is misleading, because accessing a weak
property is many times slower than a strong
one!
All that’s needed to access a strong, nonatomic
property is just to read and return the pointer value.1
Accessing a weak, nonatomic
property requires first testing that the object hasn’t been marked as deallocated, then retaining and autoreleasing the value (otherwise the returned pointer may turn invalid at any time); and each part of this access requires locking.2
The only reason in support of weak
outlets is that they don’t require explicit clean-up when releasing the top-level view.
But unloading the top-level view turned out to be difficult to get correct, difficult to test, and very difficult to keep correct as the code changes.
So Apple very practically decided to stop doing it as of iOS 6.3
Curiouser
At some point in 20114, the guidelines for iOS was changed from strong
to weak
.
I’d really like to know the history of this, because the reasons stated now were just as valid then.
Perhaps there’s some unstated reason?
Prior to ARC, the rules for managing nib objects are different from those described above. How you manage the objects depends on the platform and on the memory model in use. Whichever platform you develop for, you should define outlets using the Objective-C declared properties feature.
[…]
- For iOS, you should use:
@property (nonatomic, retain) IBOutlet UserInterfaceElementClass *anOutlet;
- For OS X, you should use:
@property (assign) IBOutlet UserInterfaceElementClass *anOutlet;
Share Your Thoughts
Do you have strong feelings on this? Am I missing something mind-numbingly obvious? Shoot a Twitter-gram to the germinal @dev_etc, or an App.net-o-gram to @jmah
-
See the objc-accessors.mm source file ↩
-
See
objc_loadWeak
in clang’s Automatic Reference Counting documentation. ↩ -
See WWDC 2012 Session 200: “What’s New in Cocoa Touch”, 00:18:00 in. ↩
-
Actually 2011-10-12, thanks to the revision history ↩