IBOutlets as weak references are problematic to work with.

Originator:zach
Number:rdar://15531971 Date Originated:November 21, 2013
Status:Open Resolved:
Product:iOS SDK Product Version:7.0.4
Classification:Other Problem Reproducible:N/A
 
Summary:
When using Interface Builder and code, you have IBOutlets. If your project is ARC, the IBOutlets generated are `weak`. This is problematic because if you turn on CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK and CLANG_WARN_OBJC_RECEIVER_WEAK to try and catch bugs, you run into dozens/hundreds/thousands of warnings in your project. While it is nice to have your code as correct as possible, seeing thousands of warnings is enough to greatly sadden pretty much anyone.

Steps to Reproduce:
Given the following interface:
@interface MYViewController : UIViewController
@property (atomic, weak) IBOutlet UILabel *readMeLabel;
@end
 
The naive implementation would look something like this:
@implementation MYViewController
- (void) viewDidLoad {
	[super viewDidLoad];
 
	// This is a contrived example. Pretend it does something more that can't be done in IB.
	self.readMeLabel.text = @"Lorem Ipsum";
	self.readMeLabel.font = [UIFont boldSystemFontOfSize:24.0f];
}
@end

This code looks relatively correct. You have an IBOutlet, and once everything is loaded, you access your property and set some values on it. Nothing extraordinary.

However! If you turn on CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK and CLANG_WARN_OBJC_RECEIVER_WEAK, you will get warnings in this code. This code will probably never not work. But, it is pedantically incorrect.

It turns out, a more correct version of this code is:
@implementation MYViewController
- (void) viewDidLoad {
	[super viewDidLoad];
 
	__strong UILabel *strongReadMeLabel = self.readMeLabel;
	strongReadMeLabel.text = @"Lorem Ipsum";
	strongReadMeLabel.font = [UIFont boldSystemFontOfSize:24.0f];
}
@end

Where you have a strong local reference to your IBOutlet and then set all of the properties.

This code is more pedantically correct, but also more tedious to write. Perhaps not in the contrived case, but, it isn't hard to imagine a case where you can have a dozen IBOutlets in a VC and want to animate them all, and then do something else once they finish animating - and then slowly animate them all another way after the user does something. Having to make a dozen strong references every time gets really old really fast. 

Expected Results:


Actual Results:


Version:
7.0.4 / Xcode 5

Notes:
From what I can gather, the reason behind IBOutlets being weak is to allow them to automatically be nil'd out when the view is unloaded. However, as of iOS 6, the view will never be unloaded automatically anymore. While I can understand being cautious, I can't help but to imagine that making IBOutlets strong references again (as they were in iOS/MRR when they were retained) would be a better pattern to use.

It would be great if this idiom was examined again, as this doesn't feel like a particularly good pattern to have. It encourages duplicate code, and since CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK and CLANG_WARN_OBJC_RECEIVER_WEAK are off by default, it means that virtually everyone (internally and externally) has incorrect code that could potentially break under unknown conditions in their project.

I can see two solutions here: one is to go back to `strong` references. This does what people want it to do, and, given the lack of view unloading, there is no reason for the view to go awa

Configuration:


Attachments:

Final thoughts:

The common pattern is a UIViewController that has a view, which in turn has IBOutlets as subviews. In this case, there is no cycle being created, as the IBOutlet/object being weakly referenced already has its lifespan controlled by the object that is modifying it. That is, idiomatic code will not break by making IBOutlets strong references. Please don't punish people who are doing it right to try and implicitly help people who are doing things badly.

Maybe a better solution to the uncommon case of nibs referencing each other is to have Xcode detect this automatically and warn the user when the static analyzer is run?

I lied, sorry. I have more thoughts on this:

it feels like `weak` is being overloaded here. There’s the usage of it to break cycles in blocks, which the warnings are trying to help with, and the usage of `weak` to say that something else owns it, which the warnings generate noise for.

To update my original ticket that was cut off (character limits in bug reports are incredibly frustrating, by the way):

Under the "Notes" section:

I can see two solutions here: one is to go back to `strong` references. This does what people want it to do, and, given the lack of view unloading, there is no reason for the view to go awa

should read:

I can see two solutions here: one is to go back to `strong` references. This does what people want it to do, and, given the lack of view unloading, there is no reason for the view to go away without the IBOutlets going away anymore. Historical reasons should stay in the past! The other is a compiler change of some sorts. Either to suppress the problem and hide the warning for IBOutlets, or, to fix the problem in a more generalized way and implicitly add precise lifetime semantics for the local scope when accessing weak references[1]. Local stores are super cheap, and, given how big this problem actually is, it would be nice to get it fixed.

1. http://clang.llvm.org/docs/AutomaticReferenceCounting.html#precise-lifetime-semantics

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!