计算中的自我调用 属性

self invocations in computed property

为什么之前我们在像这个例子这样的计算 属性 中调用 self 时,我们需要编写惰性变量,但现在我们不必这样做。为什么?

   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
    }()

我认为存在一个误解,也就是你在代码片段中提到的是不是计算属性 !它只是一个已被闭包初始化的 stored 属性正如Swift初始化中提到的—— 使用闭包或函数设置默认 属性 值:

If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property. Whenever a new instance of the type that the property belongs to is initialized, the closure or function is called, and its return value is assigned as the property’s default value.

您可以检查:.

请注意,pauseButton 的闭包甚至会在不使用它的情况下执行,如果您尝试检查它(在其中添加断点),您会注意到这一点。我认为这是 而不是 您的期望 - 而不是您的目标 - 因此您应该将其声明为 lazy var 而不是 let.

但是,

指同一个Swift documentation:

If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. You also cannot use the implicit self property, or call any of the instance’s methods.

暗示:

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)

你会得到以下错误:

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

为了不纠结于此类问题,我坚持使用旧方法并仅在惰性存储属性中使用 button.addTarget