Я хотел бы отслеживать изменение значения свойства управляемого объекта в Базовых данных с целью обеспечения целостности данных (например, при изменении указанного свойства c другой объект в отношении должен быть соответственно изменен). Я думаю реализовать это с помощью KVO и позволить управляемому объекту наблюдать за изменением своего свойства. Ниже мой код.
extension Book {
override public func awakeFromInsert() {
super.awakeFromInsert()
observation = self.observe(\.date, options: [.old, .new]) { book, dateChange in
print("date is changed!")
}
}
override public func awakeFromFetch() {
super.awakeFromFetch()
observation = self.observe(\.date, options: [.old, .new]) { book, dateChange in
print("date is changed!")
}
}
}
(Примечание: переменная observation
является временным атрибутом управляемого объекта, чтобы сохранить сильную ссылку на объект NSKeyValueObservation
.)
Мой вопрос заключается в том, что подходит место для удаления наблюдателей? Или это вообще нужно? Я попробовал приведенный выше код, и он работал нормально. Но я не уверен, что это только сработало и может иметь потенциальную проблему, когда код становится сложным.
Я выяснил следующее после некоторых исследований:
1) Приложение должно удалить KVO наблюдатели до освобождения НСОобъекта. Например, do c говорит:
Объект, вызывающий этот метод, должен также в конечном итоге вызвать либо removeObserver (: forKeyPath :), либо removeObserver (: forKeyPath: context :) метод для отмены регистрации наблюдателя при участии в KVO.
Примечание: do c касается addObserver()
, не API, который я использую, но я думаю, что идея та же.
2) Новый API KVO в Swift автоматически удаляет наблюдателя, когда объект NSKeyValueObservation
деинициализирован (т. е. когда он находится вне области действия или на него не ссылаются другие).
Итак, я подумал, что должен сделать это:
extension Book {
deinit {
observation = nil
}
}
Но, к сожалению, это невозможно, потому что XCode выдал эту ошибку:
Deinitializers may only be declared within a class
Так что мне интересно, как обычно это сделать? Может ли быть так, что, поскольку наблюдатель и наблюдающий являются одним и тем же объектом, базовый код структуры KVO замечает, что объект наблюдателя также освобождается, так что все в порядке, без явного удаления наблюдателя?
Будем благодарны за любые предложения.
Обновление 1:
@ pbasdf предлагает использовать willTurnIntoFault
. Я гуглю об этом и нахожу несколько обсуждений, которые указывают на людей, использующих это таким образом. Мое главное недоразумение: является ли неисправность обязательным состоянием до освобождения NSManagemObject
? Я не считаю, что Apple do c говорит об этом (у do c нет диаграммы перехода состояний).
РЕДАКТИРОВАТЬ: Согласно этому посту , когда NSManagedObjectContext прерывается, он превращает все зарегистрированные в нем объекты обратно в отказы. Если это так, то неисправность всегда возникает до освобождения.
Кстати, при чтении Apple do c я также нахожу это:
Когда Core Data поворачивается В случае отказа объекта уведомления об изменении значения ключа (KVO) отправляются в свойства объекта. Если вы наблюдаете свойства объекта, который превращается в ошибку, и ошибка впоследствии реализуется, вы получаете уведомления об изменении свойств, значения которых фактически не изменились.
Это заставляет меня задуматься, используя KVO с NSManagemObject
, возможно, не идеальный подход, поскольку ему мешает ошибка.
Обновление 2:
Поскольку наблюдатель и наблюдаемый - это один и тот же объект в моем В этом случае гораздо более простой подход, чем использование КВО. Это определяет оболочку метода установки свойств и реализует все логи c в оболочке. Я буду использовать этот подход.
Тем не менее, я все еще думаю, что мой вопрос верен в общем случае, когда наблюдатель и наблюдающий являются разными объектами.
Обновление 3:
@ matt отметил, что нормально не удалять наблюдателей KVO начиная с iOS11. См. Еще одно обсуждение здесь и официальное занятие c здесь .
Расслабленные требования по отмене регистрации значения ключа
До 10.13, KVO сгенерирует исключение, если какие-либо наблюдатели все еще будут зарегистрированы после того, как -deallo c объекта автоответчика завершит работу. Кроме того, если бы все наблюдатели были удалены, но некоторые были удалены из другого потока во время транзакции c, исключение было бы по-прежнему неправильно выброшено. Это требование было ослаблено в 10.13 при соблюдении двух условий:
- Объект должен использовать автоответчик KVO, а не вызывать вручную -will и -didChangeValueForKey: (т.е. он не должен возвращать NO из + automaticNotifiesObserversForKey NO автоматически) :)
- Объект не должен переопределять (частные) средства доступа для внутреннего состояния KVO
Если все они верны, все остальные наблюдатели после возврата -deallo c будут очищен КВО; это также несколько более эффективно, чем повторный вызов методов -removeObserver.