Свойства с наблюдателями (property observers) в Swift позволяют реагировать на изменения значения свойства. Эта концепция появилась вместе с Swift для повышения прозрачности контроля над изменениями данных и автоматизации логики при изменениях.
История вопроса:
До Swift подобное поведение часто реализовывалось вручную через setter'ы или специальные методы. В Swift наблюдатели появились как стандартный инструмент в языке.
Проблема:
Разработчику важно уметь отлавливать момент изменения свойства — например, для обновления UI или валидации данных. Без наблюдателей код становился бы более громоздким и менее читаемым.
Решение:
В Swift доступно два основных наблюдателя:
willSet вызывается перед изменением значенияdidSet вызывается после присваивания нового значенияНаблюдатели можно добавить к stored property, кроме lazy свойств и вычисляемых (computed) свойств.
Пример кода:
class Temperature { var celsius: Double { willSet { print("Скоро установится значение", newValue) } didSet { print("Значение было:", oldValue, ", а стало:", celsius) } } init(celsius: Double) { self.celsius = celsius } } let temp = Temperature(celsius: 22) temp.celsius = 28 // Выведет willSet и didSet
Ключевые особенности:
Можно ли добавить наблюдатели к вычисляемым (computed) свойствам?
Нет, к computed свойствам нельзя добавить willSet и didSet, поскольку у них уже есть собственные геттер и сеттер. Использование willSet/didSet актуально только для stored свойств.
Вызовутся ли наблюдатели при изменении свойства внутри инициализатора?
Нет, property observers не вызываются во время инициализации (init). Они срабатывают только при изменении значения после завершения init.
Что произойдет при присваивании текущего значения самому себе, например x = x?
Наблюдатели все равно вызовутся, даже если значение свойства не изменилось! Это может привести к побочному выполнению логики — будьте внимательны.
var value: Int = 0 { didSet { print("didSet всегда вызывается!") } } value = value // didSet вызовется
Разработчик реализовал в didSet обновление интерфейса, но не контролировал изменение значения. UI обновлялся даже при "пустом" присваивании, что снизило производительность.
Плюсы:
Минусы:
В didSet проверялось равенство старого и нового значения. Обновление интерфейса происходило только если значение действительно изменилось.
Плюсы:
Минусы: