如何监听ObservableObject

18

好的,所以在 iOS 13 上,SwiftUIObservableObject。我有一个实现 ObservableObjectModel

class Model: ObservableObject {
    @Published public var toggle: Bool = false

    init() {
        NSLog("Model init")
        objectWillChange.sink { void in
            NSLog("1 toggle \(self.toggle)")
        }
        $toggle.sink { v in
            NSLog("2 toggle \(self.toggle) -> \(v)")
        }
    }
}

并且有一个可以切换 toggle 的按钮:

struct ContentView: View {
    @ObservedObject var model: Model
    
    var body: some View {
        Button(action: {
            self.model.toggle.toggle()
        }, label: {Text(model.toggle ? "on" : "off")})
    }
}
现在,这个工作了。你点击按钮,它在“开”和“关”之间切换。(在将toggle设置为@Published之前,它不起作用。)但是,日志记录并不按预期工作。我在启动时立即获取两个日志:“Model init”和“2 toggle false->false”。虽然点击按钮似乎改变了toggle的值,但没有导致任何一个闭包执行。

当视图改变模型时,我希望有一种方式通知更改,以防需要更新计算值或同步到磁盘等操作。也许sink方法是错误的?

@Published字段的ObservableObject如何在其字段更新时得到通知?

2个回答

26

关于sink函数返回值的最新文档:

/// - 返回:可取消的实例;在您结束接收值的赋值时使用。结果的释放将拆除订阅流。

实际上,这意味着sink会创建一个Subscriber但不会保留它。一旦您完成初始化,订阅者就会被拆除并从内存中删除。您需要通过创建强引用来保存它们,例如:

class Model: ObservableObject {
    @Published public var toggle: Bool = false

    var changeSink: AnyCancellable?
    var toggleSink: AnyCancellable?

    init() {
        NSLog("Model init")
        changeSink = objectWillChange.sink { void in
            NSLog("1 toggle \(self.toggle)")
        }
        toggleSink = $toggle.sink { v in
            NSLog("2 toggle \(self.toggle) -> \(v)")
        }
    }
}

我并没有使用过太多的Combine,但是我经常看到另外一种替代方法,你可以考虑只是给你的属性添加一个didSet,像这样:

我对Combine的使用不多,但是一个替代方案是将didSet添加到属性中:

    public var toggle: Bool = false {
        didSet {
            print("1 toggle \(self.toggle)")
        }
    }

啊,这就是我所缺少的!谢谢!我可能会在某个时候使用didSet,但也很好知道如何使用objectWillChange一次性监控所有内容。objectDidChange也许也不错,但我认为默认情况下我们没有这个选项。 - Erhannis

6
您的ObservableObject类“Model”已经正确完成,但是:
1. ObjectWillChange 应该是 ObservableObjectPublisher() 类型。
这将创建一个 objectWillChange 属性,作为 ObservableObjetPublisher 的实例。这来自 Combine 框架,因此您需要添加 import Combine 以使代码编译。ObservableObject发布者的工作很简单:每当我们想要告诉世界我们的对象已经改变时,我们请求发布者代表我们执行操作。
2. 您需要观察的属性(Toggle)应该按照以下方式实现:
 var toggle = "" {
        willSet {
            objectWillChange.send()
        }
    }

第二步,我们在Model的Toggle属性上附加了一个willSet属性观察器,以便我们可以在值更改时运行代码。在我们的示例代码中,每当toggle更改时,我们调用objectWillChange.send(),这就是告诉objectWillChange发布者发出消息,我们的数据已更改,以便任何订阅的视图都可以刷新。
第三步,请确保您的Model类符合ObservableObject,并且其实例标记为@ObservedObject。由于您的Model类符合ObservableObject,因此您可以像使用任何其他@ObservedObject属性一样使用它。因此,我们可以像这样使用它来监视toggle:
struct ContentView: View {
    @ObservedObject var model: Model

    var body: some View {
        Button(action: {
            self.model.toggle.toggle()
        }, label: {Text($model.toggle ? "on" : "off")})
    }
}

希望这可以帮到您, 参考资料: https://www.hackingwithswift.com/quick-start/swiftui/how-to-send-state-updates-manually-using-objectwillchange

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接