【问题标题】:Notifications not sent when changing an ObservedObject更改 ObservedObject 时未发送通知
【发布时间】:2021-12-06 14:08:05
【问题描述】:

我有以下代码:

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

,通知永远不会触发:

你们中的任何人都知道为什么通知永远不会触发吗?或者我怎样才能做到这一点?

【问题讨论】:

  • 将您的 Stuffclass 更改为 struct
  • @OOPer 为什么这很重要?是不是因为类实例突变没有设置引用?
  • @matt 在MyStuff 属性中的MyStuff 中发布的值是一个指向对象的指针。 @ObservableObject 只会在该指针被替换时触发通知。不观察该指针后面的对象的属性,只观察指针值本身。

标签: swift swiftui combine observableobject


【解决方案1】:

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

@Published 属性需要是“纯”值类型,如果您想在其内容发生更改时收到通知。 “纯”是指仅由其他值类型组成的值类型,这些值类型也是“纯”的。

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

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

查看这段代码sn-p:

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 数据结构中的任何更改。

但是,上述内容不适用于类。类具有标识,它们具有固定的内存位置,与值类型不同,值类型在使用时会被复制到所有地方。这意味着它们内部结构的变化不会反映在上游,因为类实例存储不会改变。

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

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

【讨论】:

    【解决方案2】:

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

    我修好了:

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

    【讨论】:

    • 不,你没有抓住重点,因为如果你现在doSomething.observedObject.stuff?.str = "Doing something else"仍然不会收到“通知”。
    • @matt 你是对的。你知道如何解决这个问题吗?
    • 你读过cmets吗?你已经被告知了两次答案。
    • 我明白是因为类实例突变没有设置引用。但我的问题是如何在 Stuff 中引用 str ? @马特
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 2020-10-22
    相关资源
    最近更新 更多