KVO не работает для пользовательского свойства NSManagedObject

У меня есть подкласс 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 в собственность в надежде, что это начнет работать, но, увы, никаких результатов.

Изменить: если я создаю DynamicProperty на internalAvailability вместо availability, все работает плавно..

1 ответ

Решение

Добавление в качестве ответа, так как это стало учебным упражнением. Надеюсь, кому-то еще это будет полезно.

Приложение использует архитектуру множественного управляемого ObjectContext(moc). 1 приватный moc для внесения изменений и 1 основной moc потока, который синхронизируется с помощью mergeChanges.

В приведенном выше коде, navigationItem использует экземпляр папки, хранящийся в main-moc. DynamicProperty прослушивает изменения KVO в экземпляре папки этого основного moc. Давайте назовем эту главную папку. Когда я делаю изменения, я изменяю экземпляр папки, который мы имеем на private-moc. Давайте назовем это private-folder.

Об изменении приватной папки и вызове save на private-moc, уведомление об имени NSManagedObjectContextDidSave транслируется. main-moc синхронизируется с помощью mergeChanges.

mergeChanges изменяет основную папку, но обратите внимание, что она никогда не вызовет computed-property-setter availability , Это напрямую меняет internalAvailability ,

И, таким образом, никаких уведомлений KVO о нашем вычисленном имуществе не публикуется.

TL; DR При выполнении KVO для подкласса NSManagedObject используйте хранимое свойство вместо вычисляемого. Если у вас есть сценарий multi-moc (контекст управляемого объекта) и вы используете mergeChanges для синхронизации, setter для вашего вычисляемого свойства не вызывается при синхронизации.

Редактировать (решение): добавить метод шаблона keyPathsForValuesAffecting<KeyName> КВО соответствующая документация

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

При использовании Core Data мы используем NSManagedObjectContextObjectsDidChangeуведомление вместо КВО. Это дает много преимуществ, включая объединение событий изменений и поддержку отмены. Если нам нужно знать, какие атрибуты изменились на объекте, мы можем изучитьchangedValuesForCurrentEvent который даже включает временные атрибуты, у которых есть соответствующие keyPathsForValuesAffecting.... Эти преимущества, вероятно, перевешивают преимущества инфраструктуры привязки KVO, также известной как реактивная.

Другие вопросы по тегам