为什么在早期我们在像这个例子的计算属性中调用self时,我们需要编写lazy var,但现在我们不必这样做。为什么呢?
let(lazy var in earlier times) pauseButton: UIButton = {
let button = UIButton(type: .system)
let image = UIImage(named: "pause")
button.setImage(image, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = .white
button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
return button
}()
答案 0 :(得分:4)
我认为存在误解,您在代码段中提到的不是计算属性!它只是一个存储属性,它已被一个闭包初始化; As mentioned in the Swift Initialization - Setting a Default Property Value with a Closure or Function:
如果存储属性的默认值需要一些自定义或 设置,您可以使用闭包或全局函数来提供 该属性的自定义默认值。每当一个新的实例 属性所属的类型是初始化的,闭包或 调用函数,并将其返回值指定为属性 默认值。
您可以查看:Difference between computed property and property set with closure。
请注意,pauseButton
的关闭将在不使用它的情况下执行,如果您尝试检查它(在其中添加断点),您会注意到。我认为这是不您期望的是什么 - 而不是您的目标 - 所以您应该将其声明为lazy var
而不是let
。
然而,
参考相同的Swift documentation:
如果使用闭包初始化属性,请记住其余部分 该实例尚未初始化 关闭执行。这意味着您无法访问任何其他 封闭内的属性值,即使这些属性也是如此 有默认值。 您也无法使用隐式
self
属性, 或调用任何实例的方法。
暗示:
class MyViewController: UIViewController {
let btnTitle = "pause"
let pauseButton: UIButton = {
let button = UIButton(type: .system)
let image = UIImage(named: btnTitle)
button.setImage(image, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = .white
button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
return button
}()
func handlePause() { }
}
将在let image = UIImage(named: btnTitle)
上显示错误:
这也适用于任何其他实例成员,例如,如果您尝试将view.addSubview(button)
添加到闭包中,则view
实例成员将收到相同的错误。
但是有一个原因(我不知道为什么),使用选择器似乎是一个特例,因为button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
对我来说很好(Xcode 9.0),但是如果你试图将self
添加到其中,如下:
button.addTarget(self, action: #selector(self.handlePause), for: .touchUpInside)
您会收到以下错误:
答案 1 :(得分:0)
如果在常规存储属性中使用 button.addTarget
,则不会出现编译时错误。但我很确定这是一个错误。我通过实验发现,在 iOS 14.2 及更高版本中,常规存储属性中的选择器会导致不可预测的结果。选择器可以被释放,或者在存储的属性闭包中给出的任何其他选择器可以与按钮相关联。因此,点击一个按钮可能会触发一个旨在由另一个按钮触发的操作。
为了避免这些问题,我坚持老方法,只在惰性存储属性中使用 button.addTarget
。