修改ObservedObject嵌套属性时通知不触发的原因及解决方法咨询
问题原因
ObservableObject 协议的默认自动通知机制,以及 @Published 属性包装器的触发逻辑,都仅在被修饰的属性本身发生赋值操作时才会生效:
- 你代码中的
Stuff是引用类型(class),MyStuff中@Published修饰的stuff属性实际存储的是Stuff实例的内存地址 - 当你修改
stuff?.str、stuff?.num这类嵌套属性时,stuff本身的内存地址没有发生变化,相当于没有对stuff做赋值操作,因此@Published不会触发通知,上层的订阅自然不会回调。
解决方案
下面是三种常用的实现方案,按优先级从高到低排序:
- 方案1:将
Stuff改为值类型(struct)【优先推荐】
值类型的特点是只要内部属性发生修改,整个实例的值就会发生变化,触发外层@Published的赋值检测,自动发送通知。修改后代码如下:
// 把class改成struct即可 struct Stuff { var str: String? var num = 0 } // 其余MyStuff、DoSomething的代码不需要改动
修改嵌套属性时就会自动触发所有订阅回调。
- 方案2:
Stuff必须保留为class的场景,手动桥接通知
让Stuff也遵守ObservableObject协议,内部属性添加@Published修饰,同时在MyStuff中监听stuff的变化,转发通知到外层:
import Combine class Stuff: ObservableObject { @Published var str: String? @Published var num = 0 } class MyStuff:ObservableObject { @Published var stuff:Stuff? @Published var numer: Int? private var cancellableBag = Set<AnyCancellable>() init() { // 监听stuff的objectWillChange,转发到当前实例的objectWillChange $stuff .compactMap { $0?.objectWillChange } .switchToLatest() .sink(receiveValue: { [weak self] _ in self?.objectWillChange.send() }) .store(in: &cancellableBag) } } // DoSomething代码无需改动
- 方案3:临时手动触发通知【仅应急使用,不推荐】
每次修改Stuff嵌套属性之后,手动调用objectWillChange发送通知:
let doSomething = DoSomething() // 修改前手动发通知 doSomething.observedObject.objectWillChange.send() doSomething.observedObject.stuff?.str = "Doing something" doSomething.observedObject.stuff?.num = 2
该方案容易遗漏调用,后续维护成本高,仅适合临时调试或极少修改的场景。
内容的提问来源于stack exchange,提问作者user2924482
相关产品推荐
相关产品推荐

