Допустим, подкласс переопределяет унаследованное хранимое свойство, добавляя ему willSet и didSet. Какие наблюдатели сработают при изменении этого свойства через экземпляр подкласса?
При изменении свойства через экземпляр подкласса сработают наблюдатели подкласса и исходные наблюдатели суперкласса, если они были объявлены. Переопределение не заменяет поведение исходного свойства, а дополняет его.
Наблюдатели свойств нужны, чтобы реагировать на изменение состояния без переноса логики в каждый участок кода, где выполняется присваивание. В сочетании с наследованием это позволяет подклассу добавлять реакцию на изменение унаследованного свойства, сохраняя поведение базового класса.
Ошибка возникает, когда разработчик считает, что override полностью заменяет исходное свойство. Тогда можно случайно пропустить валидацию, синхронизацию или уведомление, реализованные в суперклассе.
Наблюдатели не превращают свойство в независимую копию. Подкласс по-прежнему работает с тем же унаследованным состоянием, а его наблюдатели становятся дополнительной логикой вокруг изменения.
Хранимое свойство суперкласса можно переопределить в подклассе, добавив willSet и didSet. При записи Swift вызывает наблюдатели обоих уровней: исходные наблюдатели суперкласса и добавленные наблюдатели подкласса.
Минимальный пример:
Переопределение возможно только для унаследованного свойства. Нельзя использовать такую запись, чтобы добавить наблюдатели к собственному свойству класса в том же объявлении: для этого наблюдатели указывают непосредственно при его объявлении.
Наблюдатели не вызываются при начальной инициализации собственных свойств экземпляра. Это защищает объект от реакции на промежуточное состояние, когда инициализация ещё не завершена. После завершения инициализации обычные присваивания свойству вызывают соответствующие наблюдатели.
Практически важно не строить критическую логику на неочевидном порядке выполнения наблюдателей разных уровней. Наблюдатели должны быть по возможности независимыми и не предполагать, что другой наблюдатель уже изменил связанное состояние.
В базовом классе модель обновляет кэш в didSet свойства status. Подклассу нужно дополнительно отправлять событие в журнал аналитики. Вариант с повторением логики в отдельном методе неудобен: любой прямой доступ к свойству может обойти этот метод.
Можно переопределить свойство в подклассе и добавить собственный didSet. Это сохраняет обновление кэша базового класса и добавляет аналитику, но увеличивает связанность между классами. Другой вариант — вынести уведомление в отдельный метод или использовать делегат; он лучше при сложной бизнес-логике, но требует дисциплины вызовов и дополнительной инфраструктуры.
Для небольшой локальной реакции выбранное переопределение с наблюдателем оправдано. Если наблюдатель начинает запускать сетевые операции, менять другие свойства или зависеть от порядка инициализации, лучше перейти к явному событийному механизму.
Заменяет ли наблюдатель подкласса наблюдатель суперкласса?
Нет. При записи через подкласс вызываются наблюдатели обоих уровней. override переопределяет интерфейс свойства для подкласса, но не отменяет исходные наблюдатели автоматически.
Вызываются ли наблюдатели при создании экземпляра подкласса?
Не для обычной начальной инициализации собственных свойств. Присваивание начального значения при формировании объекта не считается последующим изменением, на которое нужно реагировать. Наблюдатели предназначены для записей после того, как объект уже инициализирован.
Можно ли таким способом добавить наблюдатели к вычисляемому свойству суперкласса?
Да, унаследованное вычисляемое свойство также можно переопределить с наблюдателями, если его контракт позволяет запись. Но наблюдатели не делают свойство хранимым и не добавляют место для хранения значения: фактическое чтение и запись по-прежнему выполняются реализацией свойства суперкласса.