【问题标题】:KVO not working for custom property of NSManagedObjectKVO 不适用于 NSManagedObject 的自定义属性
【发布时间】:2018-10-04 09:47:19
【问题描述】:

我有一个 NSManagedObject Folder 的子类,状态为 Availability

@objc enum Availability: Int16 {
  case unknown
  case available
  case unavailable
}

每当可用性发生变化时,文件夹都必须执行额外的操作(例如删除相关文件)。所以我有

  1. internalAvailability 保存在核心数据中
  2. 使用上述属性计算属性availability

`

extension Folder {
  @NSManaged private var internalAvailability: Availability
}

extension Folder {
  private func deleteFiles(...) {
  ...
  }

  @objc dynamic public var availability: Availability {
    get {
      return internalAvailability
    }
    set {
      willChangeValue(forKey: "availability")
      deleteFiles()
      internalAvailability = newValue
      didChangeValue(forKey: "availability")
    }
  }
}

使用 Reactive,我想根据可用性更改导航项的标题,但信号永远不会被调用一次!

```

let property = DynamicProperty<NSNumber>(object: folder, keyPath: "availability")
internalVariable = property // To have a reference of property

navigationItem.reactive.title <~ property.map { (stateNumber) -> String in
  guard let a = Availability(rawValue: stateNumber.int16Value) else {
      assertionFailure()
      return ""
  }
  let prefix = a == .available ? "" : "(Nope) "
  return "\(prefix)\(folder.name)"
}

我已明确将 KVO 合规性添加到该属性中,希望这可以开始工作,但可惜没有结果。

编辑:如果我在internalAvailability 上创建DynamicProperty 而不是availability,一切都会顺利进行..

【问题讨论】:

  • 我认为你的问题不在于 KVO,而在于 ReactiveSwift 和 ARC,&lt;~ 中缀运算符返回 Disposable,尝试像使用 @987654334 一样引用这个一次性@,这将使这个 Disposable 保持活力。
  • 那行不通。 :( 额外信息:如果我在internalAvailability 上创建DynamicProperty 而不是availability,一切都会顺利进行。
  • 我不熟悉 ReactiveSwift,但是this 相关吗?
  • 你试过用正常的KVO观察属性吗?这应该能让我们弄清楚问题是出在 KVO 实现还是 ReactiveSwift 部分。
  • 是的,我试过了,正常的 KVO 也不起作用。

标签: ios core-data key-value-observing reactive-cocoa reactive-swift


【解决方案1】:

添加作为答案,因为它已成为一项学习练习。希望其他人也能从中受益。

应用程序使用多个 managedObjectContext(moc) 架构。 1 个用于进行更改的私有 moc 和 1 个使用 mergeChanges 同步自身的主线程 moc。

在上面的代码中,navigationItem 使用的是 main-moc 保存的文件夹实例。 DynamicProperty 正在侦听此 main-moc 文件夹实例上的 KVO 更改。我们称之为主文件夹。当我进行更改时,我会修改我们在 private-moc 上的文件夹实例。我们称之为私人文件夹。

在修改私有文件夹并在私有moc上调用save时,会广播名称为NSManagedObjectContextDidSave的通知。 main-moc 使用 mergeChanges 进行自身同步。

mergeChanges 更改主文件夹,但请注意它永远不会调用计算属性设置器availability。直接改internalAvailability

因此,我们的计算属性不会发布任何 KVO 通知。

TL;DR 在 NSManagedObject 子类上执行 KVO 时,使用存储属性而不是计算属性。如果您有一个多 moc(托管对象上下文)场景并使用 mergeChanges 进行同步,则同步时不会调用计算属性的设置器。

编辑(解决方案):添加模式keyPathsForValuesAffecting&lt;KeyName&gt;KVO relevant documentation的方法

@objc class func keyPathsForValuesAffectingAvailability() -> Set<NSObject> {
  return [#keyPath(Folder.internalAvailability) as NSObject]
}

【讨论】:

    【解决方案2】:

    在使用 Core Data 时,我们使用 NSManagedObjectContextObjectsDidChange 通知而不是 KVO。这带来了许多优势,包括更改事件的合并和撤消支持。如果我们需要知道对象上的哪些属性发生了变化,我们可以检查changedValuesForCurrentEvent,它甚至包括具有匹配keyPathsForValuesAffecting... 的瞬态属性。这些优势可能超过了 KVO 绑定框架(即响应式)的优势。

    【讨论】:

    • 你能分享NSManagedObjectsDidChange的文档吗,或者你的意思是NSManagedObjectContextObjectsDidChange。另外,如果您仔细阅读答案,两个MOC中都没有使用父子关系。他们完全没有关系。
    • 我修正了这两个错误,感谢您告诉我
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-20
    • 2016-03-25
    相关资源
    最近更新 更多