计算属性中的自我调用

时间:2017-10-17 16:30:17

标签: ios swift computed-properties

为什么在早期我们在像这个例子的计算属性中调用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
    }()

2 个答案:

答案 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)上显示错误:

enter image description here

这也适用于任何其他实例成员,例如,如果您尝试将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)

您会收到以下错误:

enter image description here

答案 1 :(得分:0)

如果在常规存储属性中使用 button.addTarget,则不会出现编译时错误。但我很确定这是一个错误。我通过实验发现,在 iOS 14.2 及更高版本中,常规存储属性中的选择器会导致不可预测的结果。选择器可以被释放,或者在存储的属性闭包中给出的任何其他选择器可以与按钮相关联。因此,点击一个按钮可能会触发一个旨在由另一个按钮触发的操作。

为了避免这些问题,我坚持老方法,只在惰性存储属性中使用 button.addTarget