In sample code I have seen two different styles of declaring objects. What is the advantage of one over the other?
Both are declared as
var btn: UIButton!
Style 1:
btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.layer.borderColor = UIColor.blue.cgColor
btn.layer.borderWidth = 1
...
self.view.addSubview(btn)
Style 2:
btn = {
let b = UIButton()
b.translatesAutoresizingMaskIntoConstraints = false
b.layer.borderColor = UIColor.blue.cgColor
b.layer.borderWidth = 1
...
return b
}()
self.view.addSubview(btn)
The only advantage I currently see is that the second style makes code more legible when you have many objects. You can also collapse them in Xcode. Is there any other advantage? Doesn't the second version "cost" more resources at runtime? Which is preferable?
Thanks
Closure initialization (your second example) has three big advantages.
Advantage one: Initializing let
structs. Your example uses a UIButton
, which is a class--a reference type. If we are initializing a struct into a let
, we can not mutate it. We cannot change any of its setters, nor can we call any methods marked as mutating
once we initialize it into a let
declaration. The closure initialization allows us to do this set up before assigning into the let
-declared variable.
Advantage two: Scope. The closure we initialize with gets its own scope. It can capture variables from the enclosing scope, but variables declared within the scope are not available outside it. This means we don't have collisions on variable names. It also means that ARC could do some clean-up as our initialization completes.
Advantage three: In-line initialization of class/struct member variables. The first two advantages I listed aren't always necessary and you can usually work around them. But without closure initialization, if you wanted to initialize your button at the point it is declared, you are stuck with something like this:
class MyViewController: UIViewController {
var button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
// TODO: Set up button's properties
view.addSubview(button)
}
}
But with closure initialization, we can set those all set up at the point of declaration.
class MyViewController: UIViewController {
var button: UIButton = {
let button = UIButton()
// TODO: Set up button
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
}
}
The differences are fairly trivial in small examples but may be more significant in larger, more complex software.
In the first example, the state of btn
is temporarily invalid - until all of the property assignments are complete. In the second example, the button is completely built when assigned to btn
. In addition, the code in the second is, in effect, a factory method that could be separated out into a separate class and parameterised. Useful if a lot of similar buttons are being created.
Separating the responsibility for building buttons and other controls into specialist classes is an excellent step towards reducing the size and complexity of view controllers in iOS apps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With