I found this UIBarButtonItem target issue unconsciously when using lazy var initialization.
class ViewController: UIViewController {
lazy var barButtonItem1 = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(action1))
lazy var barButtonItem2: UIBarButtonItem = {
let barButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(action2))
return barButtonItem
}
override func viewDidLoad() {
super.viewDidLoad()
print(barButtonItem1.target, barButtonItem2.target)
}
}
The printed results showed that barButtonItem1.target was nil, and barButtonItem2.target was self, which seems crazy! I got this issue when i use barButtonItem1's lazy var writing, and then i found that barButtonItem1's action can never be called, and finally the issue was barButtonItem1.target was nil.
I don't know why this happens, however i'm pretty sure this is a bug. Does anyone know something about this? I will really appreciate it if you can explain about it.
Explanation below is my guess. And unfortunately, I don't have enough reputation to give a comment so let me give you an answer.
My guess: this is a compiler bug.
First, I crafted a small extension of UIBarButtonItem. (second parameter is not of Any?
but UIViewController?
)
extension UIBarButtonItem {
convenience init(barButtonSystemItem systemItem: UIBarButtonSystemItem, targetViewController: UIViewController?, action: Selector?) {
// call the initializer provided by UIKit
self.init(barButtonSystemItem: systemItem, target: targetViewController, action: action)
}
}
Then I tried to initialize the lazy stored variable with the code below.
class ViewController: UIViewController {
lazy var barButtonItem1 = UIBarButtonItem(barButtonSystemItem: .cancel, targetViewController: self, action: #selector(action))
override func viewDidLoad() {
super.viewDidLoad()
print(barButtonItem1.target)
}
func action() { }
}
Then compiler raised error and say
Cannot convert value of type '(NSObject) -> () -> ViewController' to expected argument type 'UIViewController?'
which suggests that compiler failed to determine that self
is of ViewController
. (The initializer provided by UIKit would compile because the second parameter is of Any?
which accepts value of type (NSObject) -> () -> ViewController
.)
But when give type annotation to the lazy variable like
lazy var barButtonItem1: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, targetViewController: self, action: #selector(action))
source code happily compiled and barButtonItem1.target
was set to self
.
I believe the type annotation helped compile. Above is the reason I guess the issue you faced is caused by a compiler bug.
See also: there are reported problems similar to the issue you faced. Both of them are concluded as a compiler bug.
Swift lazy instantiating using self
Type inference when using lazy instantiation
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