KVO 不适用于 NSManagedObject 的自定义 属性

KVO not working for custom property of NSManagedObject

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

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

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

  1. internalAvailability保存在核心数据中
  2. 使用上面的 属性
  3. 计算 属性 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 而不是 availability 上创建 DynamicProperty,一切都会顺利..

添加为答案,因为它已成为一个学习练习。希望其他人也会受益。

该应用使用多 managedObjectContext(moc) 架构。 1 个用于进行更改的私有 moc 和 1 个使用 mergeChanges.

同步自身的主线程 moc

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

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

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

因此,我们计算的 属性 没有发布 KVO 通知。

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

编辑(解决方案):添加模式的方法keyPathsForValuesAffecting<KeyName>KVO relevant documentation

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

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