Combine 如何知道 ObservableObject 实际改变了

Tre*_*vör 7 swift combine

ObservableObjectCombine 框架定义的协议有一个objectWillChange发布者属性,它让你知道这个对象的属性何时发生变化,这意味着,如果这个发布者的订阅者在收到 this will change 事件时读取它的值,他们仍然会读取更改之前的值,我试图理解的是:

  • 当您可以订阅的唯一事件似乎是will change一个时,像 SwiftUI 这样的框架如何知道该值实际上确实发生了变化(即:它们如何获得新值)?底层机制是否是Combine 框架中公开可用的API?
  • ObservableObject收到 will change 事件后,如何获取 an 的实际新值?

rob*_*off 10

SwiftUI 不知道是否ObservableObject 真的改变了。SwiftUI 信任你。如果你告诉它对象会改变(通过objectWillChange发出输出),那么 SwiftUI 会假设对象改变,并安排显示更新。

我不是 100% 确定为什么 Apple 选择objectWillChange作为信号。在 WWDC 2019 上,该属性实际上是didChange,而不是objectWillChange。他们在 Xcode 11 仍处于测试阶段时对其进行了更改。也许他们所做的是在objectWillChange发出时获取对象的状态,然后在更新屏幕时再次获取它,并以某种方式比较这些值。

至于如何在objectWillChange发出后获取对象的值,这取决于您。他们 SwiftUI 的方式是通过CFRunLoopObserver.beforeWaiting活动注册 a 。当观察者运行时,它会重新渲染ObservableObjects 已发出信号的任何视图。

请注意,如果您使用@Published属性将值存储在您的 中ObservableObject,那么您可以使用$前缀订阅该属性自己的发布者。例如:

class MyObject: ObservableObject {
    @Published var value: Int = 0
}

let object = MyObject()
let ticket = object.$value
    .sink { print("new value: \($0); old value: \(object.value)") }

object.value = 1
Run Code Online (Sandbox Code Playgroud)

Published包装出版的电流值,当你SUBCRIBE,然后每次改变时间发布新的价值。但请注意,它会存储之前发布新值,如上面的示例代码所示,输出如下:

new value: 0; old value: 0
new value: 1; old value: 0
Run Code Online (Sandbox Code Playgroud)

  • https://developer.apple.com/videos/play/wwdc2020-10040/?time=1003 WWDC2020,苹果表示,我们使用“will”的原因,“我们经常遇到的问题之一是“为什么‘will’改变?为什么不‘确实’改变了?”原因是 SwiftUI 需要知道什么时候将要改变,这样它就可以将每个改变合并成一个更新。” (6认同)
  • 苹果给出的这个理由是假的。无论您是在更改发生之前还是之后收到更改通知,您都可以合并更新。据我所知,在更改之前**通知的唯一原因是如果您想比较“之前”和“之后”的值 - 这与合并不同。 (3认同)
  • 正如我所说,SwiftUI 注册了一个运行循环观察器来对更改的值进行操作。因此,您必须在观察者运行之前更改该值。这意味着您必须在将控制权返回到主运行循环之前更改该值。这实际上意味着您应该在触发“objectWillChange”后立即进行更改。 (2认同)

Gil*_*roy 5

我所做的就是添加到每个 ObservableObject 中,我需要更改后知道

public var objectDidChange = ObservableObjectPublisher()
Run Code Online (Sandbox Code Playgroud)

您可以订阅并.send()在一切更改后手动调用。


Asp*_*eri 3

ObservableObjectSwiftUI通过@ObservedObject包装器监听事件

/// A dynamic view property that subscribes to a `ObservableObject` automatically invalidating the view
/// when it changes.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper public struct ObservedObject<ObjectType> : DynamicProperty where ObjectType : ObservableObject {
Run Code Online (Sandbox Code Playgroud)

看起来就是这样DynamicProperty,这就是他们的说法

/// Represents a stored variable in a `View` type that is dynamically
/// updated from some external property of the view. These variables
/// will be given valid values immediately before `body()` is called.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol DynamicProperty {

    /// Called immediately before the view's body() function is
    /// executed, after updating the values of any dynamic properties
    /// stored in `self`.
    mutating func update()
}
Run Code Online (Sandbox Code Playgroud)

因此,ObservedObject包装器侦听已发布的事件,ObservableObject并且一旦update()调用就会提供最新值(如果需要,最有可能直接重新缓存它,而不是更新调用)。