Weak, unowned or sturdy subviews?
I’ve received various emails and tweets about this matter, so I made a decision to write down about it, as a result of it’s actually onerous to discover a correct reply for this query on the web. There are some nice posts and programming guides, some some articles are a bit older, nonetheless many individuals are asking the weak vs sturdy IBOutlet query even on the official boards, however noone actually explains the explanations, even on the boards they solely advocate this WWDC session video. So what is going on on right here? 🤔
I did some research on the subject and the very very first thing that we should always state is that this: Apple eliminated the viewDidUnload technique in iOS6 and from that model the iOS view controller lifecycle modified a bit. If you do not know a lot in regards to the lifecycle strategies (demystified), you must learn this text. This was fairly a giant change and Apple additionally touched their inner view administration. Earlier than iOS6 it was a typical observe to outline weak subviews. As a result of that they had a powerful reference to it and so they weren’t releasing it except you eliminated it from the view hierarchy.
This was about 10 years in the past. Now why are we nonetheless afraid of sturdy subviews? The primary purpose was the addSubview technique. The documentation states that it will create a powerful reference, which routinely triggered my mind and I outlined my views as weak pointers, since they are going have a powerful reference to their mother and father. Appears cheap, proper? 🧠
Weak subviews
Effectively, the issue is that if you wish to outline a weak variable we now have to make use of an non-compulsory, however I do not like the concept of utilizing an non-compulsory variable for the reason that view goes to be at all times there, it is a part of the view hierarchy in some unspecified time in the future in, it is not going wherever. It is solely going to be “destroyed” when my view controller is deallocated. Ought to I declare it as an implicitly unwrapped non-compulsory?!? Possibly.
import UIKit
class ViewController: UIViewController {
weak var foo: UILabel!
weak var bar: UILabel?
override func viewDidLoad() {
tremendous.viewDidLoad()
foo.removeFromSuperview()
foo.textual content = "crash"
}
}
Really you’ll be able to go incorrect with unwrapped weak pointers, as a result of when you take away your view from the view hiearchy in some unspecified time in the future in time earlier than the view controller deallocation then your weak pointer can be nil. On this case there will not be any extra sturdy references and your view can be deallocated straight away, so if it is an implicitly unwrapped non-compulsory, then we now have a bother. Your app will crash when you attempt to entry the property, as a result of it’ll have a zero worth.
So sure you need to use implicitly unwrapped non-compulsory variables to retailer subviews, however solely in case you are positive that you’re not going to take away it from the hiearchy. This additionally implies that you do not belief Apple’s view administration system, which is okay, there could be bugs, however truthfully that is fairly an important function and it has been round for a decade by now. 🙃
The opposite different is to make use of an everyday weak non-compulsory variable, however in that case you may at all times need to test if it is nil or not, which goes to be a ache within the ass, however a minimum of you are going to be protected for positive. Private opinion: it will not definitely worth the effort in any respect and I by no means saved views like this.
Sturdy subviews
My suggestion is to belief Apple and outline your subviews as sturdy properties. Okay, this will also be problematic you probably have different sturdy references to the identical stuff, however typically if the view controller has the one reference to that given subview you have to be completely wonderful.
Since it is a sturdy property you additionally need to initialize the view, however that is not a giant deal. You may at all times initialize a view with a .zero body and that is it. Alternatively you’ll be able to create a subclass with an everyday init()
technique, that is even higher, becuase you will use auto format for positive and this fashion can set the translatesAutoresizingMaskIntoConstraints
property in a single go.
import UIKit
class Label: UILabel {
init() {
tremendous.init(body: .zero)
self.translatesAutoresizingMaskIntoConstraints = false
}
@obtainable(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been applied")
}
deinit {
print("deinit Label")
}
}
class ViewController: UIViewController {
var foo: Label = .init()
var bar: UILabel = .init(body: .zero)
override func viewDidLoad() {
tremendous.viewDidLoad()
}
deinit {
print("deinit ViewController")
}
}
By implementing a customized deinit technique and even higher, by making a symbolic breakpoint you’ll be able to simply detect retain cycles and repair reminiscence points. I made some checks and I can affirm you do not have to be afraid of sturdy views, each the viewcontroller and the view goes to be deallocated if it is wanted. 👻
Unowned subviews
Unowned and weak are kind of equal, I would say that you simply will not have to outline views as unowned references, as a result of they are often problematic if it involves initialization. It is normally higher to have a weak reference and test for nil values, however in fact there could be some instances the place you may want an unowned subview reference.
Utilizing loadView and viewDidLoad
The loadView technique can be utilized to create your personal views manually. You must by no means name this technique immediately, nevertheless it’s save to override it. The opposite factor that you shouldn’t is that in case you are utilizing this technique to override the foundation view, you then should not name tremendous.loadView().
import UIKit
class ViewController: UIViewController {
override func loadView() {
view = UIView(body: .zero)
}
}
In each different case whenever you simply need to add views to the view hierarchy, it is fully wonderful to name the tremendous technique. I am normally implementing this technique to setup views and constraints.
import UIKit
class ViewController: UIViewController {
var foo: Label = .init()
override func loadView() {
tremendous.loadView()
view.addSubview(foo)
NSLayoutConstraint.activate([
view.centerXAnchor.constraint(equalTo: foo.centerXAnchor),
view.leadingAnchor.constraint(equalTo: foo.leadingAnchor),
view.trailingAnchor.constraint(equalTo: foo.trailingAnchor),
foo.heightAnchor.constraint(equalToConstant: 44),
])
}
}
This manner I can ensure that each single view is prepared by the point the viewDidLoad technique is known as. It’s doable to configure views contained in the loadView technique too, however I favor to maintain the hierarchy setup there and I place all the pieces else contained in the viewDidLoad perform. I imply controller associated stuff solely, like organising navigation bar buttons and issues like this.
As I discussed this in my earlier article, I favor to make use of subclasses to configure my views, I additionally transfer format constraints there (as a perform that returns them primarily based on some parameters) to maintain the view controller clear. Contained in the viewDidLoad technique I can carry out further person interface associated actions, however that is it I do not use it for including or styling views anymore.
Conclusion
Based mostly on my present data, here’s what I like to recommend for contemporary UIKit builders:
- Outline your subviews as
sturdy
properties - All the time test for leaks, implement
deinit
, use breakpoints or devices - Use
weak
/unowned
references if it’s important to break retain cycles - Add views to the hierarchy within the
loadView
technique - Use subclasses for styling views, make them reusable
- Outline format constraint getters on the view subclass, activate them inside
loadView
- Carry out remaining UI associated operations within the
viewDidLoad
perform
That is it. I am not saying that is the right strategy, however for me it is positively the way in which to go ahead with UIKit. I do know for positive that many individuals are nonetheless working with the framework and it’s right here to remain for a very long time. I hope the following tips will assist you to grasp UIKit just a bit bit higher. ☺️