You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift属性观察者初始化调用时机咨询:基于Apple官方文档

Swift 属性观察者初始化触发时机的逻辑确认与补充细节

你的核心理解完全准确,Apple官方文档里关于属性观察者在初始化阶段的触发规则可以拆解为几个明确场景,结合代码示例会更直观:

1. 类自身初始化:父类初始化器调用前设置属性 → 不触发观察者

当你在子类初始化器中,还没调用super.init()(父类初始化器)时,无论是设置自身带willSet/didSet的属性,还是修改父类属性,这些操作都属于「初始化赋值」,不属于常规的属性修改,因此不会触发任何观察者。这是因为Swift要求子类必须先完成父类的初始化,才能进行自身的属性修改逻辑。

示例代码:

class ParentClass {
    var parentProperty: String = "default" {
        willSet { print("Parent willSet: \(newValue)") }
        didSet { print("Parent didSet: \(oldValue) → \(parentProperty)") }
    }
    
    init() { print("Parent init completed") }
}

class ChildClass: ParentClass {
    var childProperty: String = "childDefault" {
        willSet { print("Child willSet: \(newValue)") }
        didSet { print("Child didSet: \(oldValue) → \(childProperty)") }
    }
    
    init(childVal: String, parentVal: String) {
        // 父类init未调用前的属性赋值
        childProperty = childVal
        parentProperty = parentVal
        super.init()
        print("Child init completed")
    }
}

let child = ChildClass(childVal: "newChild", parentVal: "newParent")

输出结果:

Parent init completed
Child init completed

可以看到,super.init()之前的两次赋值都没有触发观察者。

2. 子类初始化:父类初始化器调用后设置父类属性 → 触发父类观察者

当super.init()调用完成后,父类已经完成初始化,此时修改父类属性就属于常规的属性更新,父类属性的willSet和didSet会被正常触发。

修改子类初始化器:

init(childVal: String, parentVal: String) {
    childProperty = childVal
    super.init()
    // 父类init完成后修改父类属性
    parentProperty = parentVal
    print("Child init completed")
}

输出结果:

Parent init completed
Parent willSet: newParent
Parent didSet: default → newParent
Child init completed

这里父类属性的观察者被成功触发。

3. 补充:父类初始化器调用后,设置子类自身属性 → 触发子类观察者

同样的逻辑,在super.init()之后修改子类自己的带观察者属性,也会触发对应的willSet/didSet:

继续修改子类初始化器:

init(childVal: String, parentVal: String) {
    super.init()
    // 父类init完成后修改自身属性和父类属性
    childProperty = childVal
    parentProperty = parentVal
    print("Child init completed")
}

输出结果:

Parent init completed
Child willSet: newChild
Child didSet: childDefault → newChild
Parent willSet: newParent
Parent didSet: default → newParent
Child init completed

两个属性的观察者都正常执行了。

4. 结构体的特殊情况(无继承)

对于结构体来说,因为没有父类初始化的概念,所以在结构体初始化器完成前的所有属性赋值,都不会触发观察者;只有当结构体实例完全初始化完成后,再修改属性才会触发。

示例:

struct TestStruct {
    var value: String = "default" {
        willSet { print("Struct willSet: \(newValue)") }
        didSet { print("Struct didSet: \(oldValue) → \(value)") }
    }
    
    init(newVal: String) {
        value = newVal // 初始化赋值,不触发观察者
    }
}

var structInst = TestStruct(newVal: "initVal")
structInst.value = "updatedVal" // 初始化完成后修改,触发观察者

输出:

Struct willSet: updatedVal
Struct didSet: initVal → updatedVal

核心规则总结

  • 类初始化:仅在父类初始化器调用完成后,对属性的修改(父类/子类属性)才会触发willSet/didSet;父类init调用前的赋值属于初始化操作,不触发观察者。
  • 结构体初始化:初始化器完成前的属性赋值不触发观察者,实例完全初始化后的修改才会触发。

内容的提问来源于stack exchange,提问作者barndog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:23:09