Swift属性观察器触发条件
Swift属性观察器触发条件

Swift属性观察器触发条件

本文实际上是《深入了解SwiftUI和Swift属性包装器的工作原理》一文的延续,延续并扩展了上一文中的属性观察器的知识。

在 Swift 中,didSet 是一个属性观察器,用于在属性的值发生变化后立即执行某些操作。它的触发条件与 Swift 属性的赋值过程密切相关。以下是详细的原理和解释:

属性赋值语法触发 didSet

属性观察器(didSet 和 willSet)是在属性的值被修改时触发的。这种修改必须通过显式的赋值语法完成,例如:

self.property = newValue

当对属性进行赋值操作时,Swift 会自动调用 didSet。这是因为属性赋值是 Swift 编译器内置的一个明确的触发点。

示例

var property: Int = 0 {
    didSet {
        print("Property changed from \(oldValue) to \(property)")
    }
}

property = 10 // 打印:Property changed from 0 to 10

赋值语法 property = 10 明确改变了属性的值,因此触发了 didSet。

为什么只通过赋值语法触发?

这是因为 didSet 是在属性存储的上下文中实现的,而不是直接监听值的变化。以下是原因:

didSet 是属性存储的附加功能

属性存储是 Swift 编译器管理的核心功能。当属性的值通过赋值语法修改时,编译器会自动插入对 didSet 的调用。

如果值变化不是通过赋值语法完成(例如直接修改底层存储或通过其他机制更新值),didSet 不会被调用,因为这些操作绕过了 Swift 的属性存储逻辑。

内部优化

Swift 属性赋值语法(self.property = newValue)封装了对存储的访问和观察器调用。

如果允许其他形式的值变化(例如直接修改底层存储)触发 didSet,则会增加运行时的复杂性和性能开销。

确保一致性和可预测性

didSet 只触发一次,且只在显式赋值时触发,这让开发者更容易理解和预测代码行为。

其他操作不会触发 didSet 的原因

以下是一些常见情况下,didSet 不会触发的原因:

初始化

在属性初始化时,didSet 不会触发。比如:

var property: Int = 0 {
    didSet {
        print("Property changed")
    }
}

let instance = MyClass() // 初始化时,didSet 不会触发

原因:

初始化是创建对象的过程,属性值还不存在,因此没有“旧值”可以比较。

直接修改底层存储

如果属性是通过某种方式直接操作底层存储,而不是通过赋值语法,didSet 不会触发。例如:

@propertyWrapper
struct MyWrapper {
    var wrappedValue: Int {
        get { return storage }
        set { storage = newValue } // 这里绕过了 didSet 的触发
    }
    private var storage: Int
}

var property: Int = 0 {
    didSet {
        print("Property changed")
    }
}

// 直接修改底层存储不会触发 didSet

属性观察器触发条件总结

会触发的情况

显式赋值:

self.property = newValue

通过 Binding 触发间接赋值(例如在 SwiftUI 中):

$property.wrappedValue = newValue

不会触发的情况

属性初始化:

var property = 10 // 不触发 didSet

直接修改底层存储:

_property.storage = newValue // 不触发 didSet

总结

didSet 的触发条件是通过显式的 属性赋值语法 (self.property = newValue) 修改值,因为:

1、Swift 属性观察器是与属性赋值逻辑绑定的。

2、只有赋值操作才能触发属性存储的变更逻辑,继而调用 didSet。

3、其他非赋值操作绕过了编译器对属性存储的管理,因此不会触发 didSet。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注