KVO无法用于NSManagedObject的自定义属性

时间:2018-10-04 09:47:19

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

我有一个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")
    }
  }
}

使用反应式,我想根据可用性更改导航项的标题,但是信号一次都不会调用!

```

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合规性明确添加到该物业中,希望它能开始工作,但可惜没有结果。

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

2 个答案:

答案 0 :(得分:2)

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

该应用程序使用多个ManagedObjectContext(moc)架构。 1个私有moc进行更改,还有1个主线程moc使用mergeChanges进行自我同步。

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

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

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

因此,不会发布有关计算所得属性的KVO通知。

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

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

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

答案 1 :(得分:1)

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