KVO не работает для пользовательского свойства NSManagedObject
У меня есть подкласс NSManagedObject Folder
с состоянием Availability
@objc enum Availability: Int16 {
case unknown
case available
case unavailable
}
Папка должна делать дополнительные вещи (например, удалять связанные файлы) всякий раз, когда меняется ее доступность. Так что я
internalAvailability
сохранено в основных данных- Компьютерная собственность
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, также известной как реактивная.