Reputation: 358
Official docs (Swift 4.1) says:
If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. You also cannot use the implicit self property, or call any of the instance’s methods.
So I guess i need to use lazy var,today I created a button
let loginRegisterButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.rgb(red: 80, green: 101, blue: 161)
button.setTitle("Register", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
button.addTarget(self , action: #selector(handleRegister), for: .touchUpInside)
return button
}()
so I just used self in addTarget method and XCode did not seem to bother even more program went without any errors. Everything worked out well.So Why can i use self when I initialize property with a closure? Is there some changes or maybe I missed something?
Upvotes: 4
Views: 1335
Reputation: 385580
Weirdly—perhaps buggily—the self
in your closure evaluates to a function object. (Hamish explained in a comment that the function is a curried form of NSObjectProtocol.self
.) However, when you later ask the button for its target, it reports +[NSNull null]
:
let loginRegisterButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = .red
button.setTitle("Register", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
print(self)
// Output: (Function)
button.addTarget(self, action: #selector(handleRegister), for: .touchUpInside)
let target = button.allTargets.first!.base
print(target, type(of: target))
// Output: <null> NSNull
return button
}()
(Tested using Xcode 9.3.1.)
The function gets turned into NSNull
as follows: the function object is allocated just before the call to addTarget
, to be passed as the target
argument. The loginRegisterButton
-creating closure holds a strong reference to the newly-allocated function.
The button stores the target as a zeroing weak reference.
After addTarget
returns, the closure releases its strong reference to the function. This was the only strong reference to the function, so Swift deallocates the function. This sets the button's weak reference to the function to nil.
When we then ask the button for allTargets
, it constructs an NSSet
. Since NSSet
cannot hold nil directly, the button puts +[NSNull null]
in the set instead.
Upvotes: 4
Reputation: 35616
The target in your code is probably nil
, the reason that this might work is that:
...If you specify nil, UIKit searches the responder chain for an object that responds to the specified action message and delivers the message to that object
(from the documentation of the method)
You can verify that the target is actually nil
by setting a breakpoint during or after your controller's init and by inspecting the _targetActions
array (in the debugger's variables view) of the button (you can see that target
's address is 0x0
).
In your example, loginRegisterButton
is set during the controller's initialization which means that there is no self
yet (in other words you cannot guarantee that all instance variables are initialized at this point). The way lazy
solves this is that the actual assignment is deferred on first access.
Upvotes: 4