iOS lazy var UIBarButtonItem目标问题

悟空之*_*空之空 5 lazy-evaluation target uibarbuttonitem ios swift

我在使用惰性var初始化时无意识地发现了这个UIBarButtonItem目标问题.

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

打印结果显示barButtonItem1.target为零,barButtonItem2.target为self,这看起来很疯狂!当我使用barButtonItem1的懒惰var写时我得到了这个问题,然后我发现barButtonItem1的动作永远不会被调用,最后问题是barButtonItem1.target为零.

我不知道为什么会这样,但我很确定这是一个错误.有谁知道这件事?如果你能解释一下,我将非常感激.

小智 3

下面的解释是我的猜测。不幸的是,我没有足够的声誉来发表评论,所以让我给你一个答案。

我的猜测:这是一个编译器错误。


首先,我制作了 UIBarButtonItem 的一个小型扩展。(第二个参数不是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)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我尝试使用下面的代码初始化惰性存储变量。

class ViewController: UIViewController {

    lazy var barButtonItem1 = UIBarButtonItem(barButtonSystemItem: .cancel, targetViewController: self, action: #selector(action))

    override func viewDidLoad() {
        super.viewDidLoad()
        print(barButtonItem1.target)
    }
    func action() { }
}
Run Code Online (Sandbox Code Playgroud)

然后编译器抛出错误并说

无法将类型“(NSObject) -> () -> ViewController”的值转换为预期的参数类型“UIViewController?”

这表明编译器无法确定它selfViewController. (UIKit 提供的初始值设定项会编译,因为第二个参数Any?接受类型的值(NSObject) -> () -> ViewController。)

但是当给惰性变量提供类型注释时,例如

lazy var barButtonItem1: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, targetViewController: self, action: #selector(action))
Run Code Online (Sandbox Code Playgroud)

源代码愉快地编译并被barButtonItem1.target设置为self.

我相信类型注释有助于编译。以上是我猜测您遇到的问题是由编译器错误引起的原因。


另请参阅:已报告与您遇到的问题类似的问题。两者都被总结为编译器错误。

使用 self 快速惰性实例化

使用延迟实例化时的类型推断