【问题标题】:NSManagedObject changes do not trigger objectWillChangeNSManagedObject 更改不会触发 objectWillChange
【发布时间】:2020-01-23 21:04:42
【问题描述】:

我有一个核心数据模型,其中一个实体生成到类Task 中。我正在尝试从NSManagedObject 获取Combine 发布者objectWillChange 发送(自动,无需手动工作),但它不会。任务实体有一个name 属性。

let task = Task(context: container.viewContext)

let taskSubscription = task.objectWillChange.sink(receiveValue: { _ in
    print("Task changed")
})

task.name = "Foo"              // WILL NOT trigger

如果我手动调用发送,订阅将起作用:

task.objectWillChange.send()   // Will trigger

如果我用简单的ObservableObject 替换它,它将按预期工作:

class DummyTask: ObservableObject {
    @Published var name: String?
}
let dummy = DummyTask()
let dummySubscription = dummy.objectWillChange.sink(receiveValue: { _ in
    print("Dummy changed")
})

dummy.name = "Foo"              // Will trigger
dummy.objectWillChange.send()   // Will trigger

NSManagedObject 有问题吗?我应该如何观察一般实体对象的变化?我应该如何让 SwiftUI 看到它们?

这是使用 Xcode 11.0 和 iOS 13。

【问题讨论】:

  • name 是否标记为已发布?这通常是我遇到问题的原因。不幸的是,我认为这里没有足够的人来帮助你。 hackingwithswift.com/quick-start/swiftui/…andrewcbancroft.com/blog/ios-development/data-persistence/… 可以帮助你
  • NSManagedObject 中的 @MichaelSalmon name 是从模型定义生成的。查看生成的 swift 文件时,它是@NSManaged。这里的重点是,尽管两者都是ObservableObjects,但两者的工作方式不同。
  • 我刚刚开始使用 Core Data,我还不能说我印象深刻。我还不确定,但在我看来,只有在保存上下文时才会触发更改。要记住的一件事是 ObservableObject 有一个名为 objectWillChange 的发布者,仅此而已。在普通的 OO 中,您添加 \@Published 或编写您自己的等价物,我不知道 \@NSManaged 做了什么,我认为您不应该假定它可以满足您的要求。另一种方法是创建子类文件并修改它们以适应。
  • 我已经到了更新核心数据条目的阶段。更新数据很轻松,但要显示更改需要一些时间。最后,我将 Core Data 实例标记为 @ObservedObject 并为该实例调用 objectWillChange.send()
  • 我看到了完全相同的问题。可悲的是,我的 NSManagedObject 可以通过后台同步刷新,我还没有找到一种方法来完成这项工作。我将继续使用旧的通知。

标签: swift core-data swiftui combine


【解决方案1】:

我相信这是一个错误。 NSManagedObject 没有意义符合 ObservableObject 但无法将任何属性标记为 @Published

虽然我们正在等待 Apple 纠正此问题,但我遇到了比 @jesseSpencer 建议的更清洁的解决方案。其背后的逻辑是相同的,通过添加objectWillChange.send(),但将全局添加到willChangeValue(forKey key: String),而不是添加到单个属性中。

override public func willChangeValue(forKey key: String) {  
    super.willChangeValue(forKey: key)  
    self.objectWillChange.send()  
}

致谢:https://forums.developer.apple.com/thread/121897

【讨论】:

    【解决方案2】:

    我的猜测是这是一个错误。 NSManagedObject 与 ObservableObject 的一致性是在 beta 5 中添加的,它还引入了其他重大更改,包括弃用 BindableObject(用于替换为 ObservableObject)。

    请参阅 SwiftUI 部分: https://developer.apple.com/documentation/ios_ipados_release_notes/ios_13_release_notes)

    我遇到了同样的问题,尽管 NSManagedObject 符合 ObservableObject,但它没有发出更改通知。这可能与需要用 @NSManaged 包装的 NSManagedObject 属性有关,而 @NSManaged 不能与 @Published 结合使用,而 ObservableObject 文档指出,默认情况下,ObservableObject 将为 @Published 属性更改合成 objectWillChange 发布者。 https://developer.apple.com/documentation/combine/observableobject

    我首先尝试通过在我的 NSManagedObject 子类中的键值方法覆盖中引导调用 objectWillChange.send() 来解决此问题,但这只会导致不正确的行为。

    如果您需要在 SwiftUI 视图中更改大量相互依赖的属性,我采用的解决方案是最简单的,但不幸的是可能是最庞大的。但是,到目前为止,它对我来说运行良好,并且可以按预期使用 SwiftUI。


    在斯威夫特中:

    1. 为您的实体创建一个 NSManagedObject 子类。
    2. 在该子类中,为您希望从 SwiftUI 视图中更改的属性创建 setter 方法,并在方法的开头添加对 objectWillChange.send() 的调用,它应该如下所示:

      func setTitle(_ text: String) {
          objectWillChange.send()
      
          self.title = text
      }
      

    我只建议将此作为临时解决方法,因为它并不理想,希望很快会得到解决。

    我将在 FeedbackAssistant 中提交错误报告,并建议遇到此问题的其他人也这样做,这样我们就可以让 Apple 再看看这个!

    编辑: 关于@Anthony 回答的警告: 虽然建议的方法确实有效,但请注意它在更改集合类型关系时不起作用,即将对象添加到与 NSManagedObject 关联的数组中。

    【讨论】:

      猜你喜欢
      • 2021-04-01
      • 2019-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-16
      相关资源
      最近更新 更多