Какое подходящее место для удаления наблюдателя КВО для управляемого объекта - PullRequest
0 голосов
/ 30 января 2020

Я хотел бы отслеживать изменение значения свойства управляемого объекта в Базовых данных с целью обеспечения целостности данных (например, при изменении указанного свойства 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.

...