更改 ObservedObject 时未发送通知

Notifications not sent when changing an ObservedObject

我有以下代码:

class Stuff {
    var str: String?
    var num = 0
}

class MyStuff:ObservableObject {
    @Published var stuff:Stuff?
    @Published var numer: Int?
}

class DoSomething {
    let observedObject = MyStuff()
    var cancellableBag = Set<AnyCancellable>()
    
    init() {
        observedObject.objectWillChange.sink { value in
            print(value)
            print("Object has changed")
        }.store(in: &cancellableBag)
        observedObject.$stuff.sink { value in
            print(value?.str ?? "")
        }.store(in: &cancellableBag )
    }
}

但是当我执行时:

let doSomething = DoSomething()
doSomething.observedObject.stuff?.str = "Doing something"
doSomething.observedObject.stuff?.num = 2

,通知永远不会触发:

你们知道为什么通知永远不会触发吗?或者我怎样才能实现它?

问题是doSomething.observedObject.stuff它是零。

我修复了:

let doSomething = DoSomething()
let stuff = Stuff()
stuff.str = "Doig Something"
doSomething.observedObject.stuff = stuff

正如评论中已经建议的那样,如果要使其工作,则需要将 class 转换为结构:struct Stuff {。结构是一种值类型,这使得它可以很好地与 ObservableObject 一起使用,而 class 是一种引用类型,但它不能很好地工作。

@Published 属性需要是“纯”值类型,如果你想在它们的内容发生变化时得到通知。 “纯”是指仅由其他值类型构成的值类型,这些值类型也是“纯”的。

为什么需要这个?这是因为 @Published 机制依赖于 willSet/didSet 属性 观察者,而 属性 观察者只有在持有的值发生变化时才会执行。

对于值类型,其结构中的任何更改都会向上游传播,从而导致整个 属性 被更新。这是由于值类型的语义造成的。

查看此代码片段:

struct Question {
    var title: String = ""
}

class Page {
    var question = Question() {
        didSet {
            print("Question changed: \(question)")
        }
    }
}

let page = Page()
page.question.title = "@Published doesn't work"

question 成员的任何更改都会导致整个 属性 被替换,这会触发 willSet/didSet 属性 观察者,因此您可以很好地监控 question.

的数据结构的任何变化

然而,上述内容不适用于 classes。 类 有身份,它们有一个固定的内存位置,不像值类型,它在使用时会被复制到各处。这意味着它们内部结构的变化不会反映在上游,因为 class 实例存储不会改变。

唯一一次 @Published 观察器被触发 classes 是在您替换对象本身时。试试这个,你会看到通知被触发:

doSomething.observedObject.stuff = Stuff() // this will print `Object has changed`