官方文档(Swift 4.1)说:
如果使用闭包初始化属性,请记住其余部分 该实例尚未初始化 关闭执行。这意味着您无法访问任何其他 封闭内的属性值,即使这些属性也是如此 有默认值。你也不能使用隐式自我属性, 或者调用任何实例的方法。
所以我想我需要使用lazy var,今天我创建了一个按钮
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
}()
所以我只是在addTarget方法中使用self而XCode似乎并没有打扰更多的程序没有任何错误。一切都很好。所以当我用闭包初始化属性时,为什么我可以使用self?是否有一些变化或者我错过了什么?
答案 0 :(得分:4)
很奇怪 - 也许是buggily-闭包中的self
计算为一个函数对象。 (Hamish在评论中解释说,该函数是NSObjectProtocol.self
的一种咖喱形式。)但是,当您稍后询问按钮的目标时,它会报告+[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
}()
(使用Xcode 9.3.1测试。)
该函数变为NSNull
,如下所示:函数对象在调用addTarget
之前分配,作为target
参数传递。 loginRegisterButton
- 创建闭包具有对新分配函数的强引用。
该按钮将目标存储为归零弱引用。
在addTarget
返回后,闭包释放其对该函数的强引用。这是该函数的唯一强引用,因此Swift释放该函数。这会将按钮对函数的弱引用设置为nil。
当我们询问allTargets
按钮时,它会构建NSSet
。由于NSSet
无法直接保留nil,因此按钮会将+[NSNull null]
放入集合中。
答案 1 :(得分:3)
您的代码中的目标是可能 nil
,这可能起作用的原因是:
...如果你指定nil,UIKit会在响应者链中搜索一个 响应指定操作消息并传递的对象 消息到该对象
(来自documentation of the method)
您可以通过在控制器初始化期间或之后设置断点并检查按钮的nil
数组(在调试器的变量视图中)来验证目标是否为_targetActions
(您可以看到) target
的地址为0x0
)。
在您的示例中,loginRegisterButton
在控制器初始化期间设置,这意味着还没有self
(换句话说,您无法保证此时所有实例变量都已初始化)。 lazy
解决这个问题的方法是在第一次访问时推迟实际的分配。