NSLayoutConstraint should not be declared weak in Xcode 10ß2 - Swift 4.2

When compiling this OSX App in XCode 10 beta 2 (Swift 4.2), I get a warning that did not show in XCode 9.4 each time I create an NSConstraint programmatically


Instance will be immediately deallocated because property 'myConstraint' is 'weak'


fileprivate weak var myConstraint    : NSLayoutConstraint!

myConstraint = NSLayoutConstraint(item: aButton, attribute: .top, relatedBy: .equal, toItem: aView, attribute: .bottom, multiplier: 1.0, constant: 30)


However, there is no such warning for IBOutlet


@IBOutlet fileprivate weak var anotherConstraint         : NSLayoutConstraint!


Is it a just a new warning for an error that existed before ? Or did something change in NSConstraint ?

Should I treat IBOutlet and programmatically created differently with respect to weak ?

Answered by QuinceyMorris in 319143022

It's different with IBOutlet.


In that case, the constraint is loaded from a NIB file. Your view controller or window controller holds a strong reference to the array of top level objects from the NIB. Those top level objects in turn hold strong child references to their subordinate objects, and so on all the way down. That means the entire object graph loaded from the NIB is kept alive by the controller, and it does not matter whether your IBOutlet is strong or weak.


This is described (in excruciating detail) in this document:


https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html


It might not be absolutely up to date, but I'm not aware of anything important that's changed in the last 2 years.


Your weak variable is a different case. As soon as the assignment is complete, there is nothing keeping the constraint alive, so it will be deallocated. If you need to keep it alive long enough to (say) add to a view, use a local variable to hold the result of creating it, assign that to your weak variable, and then add it to the view.

Accepted Answer

It's different with IBOutlet.


In that case, the constraint is loaded from a NIB file. Your view controller or window controller holds a strong reference to the array of top level objects from the NIB. Those top level objects in turn hold strong child references to their subordinate objects, and so on all the way down. That means the entire object graph loaded from the NIB is kept alive by the controller, and it does not matter whether your IBOutlet is strong or weak.


This is described (in excruciating detail) in this document:


https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html


It might not be absolutely up to date, but I'm not aware of anything important that's changed in the last 2 years.


Your weak variable is a different case. As soon as the assignment is complete, there is nothing keeping the constraint alive, so it will be deallocated. If you need to keep it alive long enough to (say) add to a view, use a local variable to hold the result of creating it, assign that to your weak variable, and then add it to the view.

I think there's more to the story that bears consideration:
  1. @IBOutlet annotation merely allows a variable to be exposed to Interface builder. It doesn't require it to actually be correctly connected to a Xib. If it were unconnected and the variable were referenced within code, the app would crash. We've all experienced this developer error.

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

2. Contrary to a popular WWDC presentation by a UIKit engineer (2015?) who stated that IBOutlets should now be strong, the Apple docs haven't changed. It still recommends @IBOutlet's should be weak var and implicitly unwrapped, since as QuinceyMorris states above, the view hierarchy does hold strong references to correctly attached IBOutlets, thus allowing the object holding the IBOutlet to do so weakly.

3. As always, the devil is in the details with a notable exception:
If a UI element is to be added or removed programmatically, it may need to be held with a strong reference because removing a weakly held IBOutlet element from the view hierarchy that currently holds it strongly would cause it to be immediately deallocated if no other references held it strongly.
NSLayoutConstraint should not be declared weak in Xcode 10ß2 - Swift 4.2
 
 
Q