При повторной записи того же значения в наблюдаемое свойство определите порядок и содержание вывода:
struct Temperature {
var celsius: Int {
willSet { print("willSet", newValue) }
didSet { print("didSet", oldValue) }
}
}
var temperature = Temperature(celsius: 20)
temperature.celsius = 20
Объясните, вызываются ли наблюдатели, если значение фактически не изменилось.
При присваивании temperature.celsius = 20 оба наблюдателя выполняются, даже если новое значение равно старому. Вывод будет таким:
willSet получает новое значение до записи, а didSet — старое значение после записи. Наблюдатели не выполняются только потому, что значения различаются: сам факт присваивания уже установленному свойству достаточен.
Наблюдатели свойств нужны для добавления реакции на изменение хранимого свойства без написания отдельного вычисляемого свойства с ручным getter и setter. Такой подход позволяет централизовать побочные действия: обновление интерфейса, журналирование, отправку уведомлений или поддержание производного состояния.
При этом наблюдатели не являются механизмом фильтрации изменений. Они уведомляют о присваивании, но не предотвращают его и не пропускают обработчик только при обнаружении отличия.
Разработчик может ошибочно ожидать, что didSet вызовется только при изменении значения. Это приводит к лишним сетевым запросам, повторной перерисовке интерфейса или ненужному пересчёту данных, если код присваивает свойству то же значение.
Важно также отличать присваивание от инициализации. Наблюдатели не вызываются при первоначальной инициализации хранимого свойства, потому что объект в этот момент ещё формируется и его состояние не считается обычным изменением.
При записи Swift выполняет операции в следующем порядке:
willSet, передавая это новое значение через newValue;didSet, передавая прежнее значение через oldValue.В примере старое и новое значения равны 20, поэтому оба вызова получают число 20. Swift не выполняет автоматическое сравнение значений перед вызовом наблюдателей: для произвольного типа такое сравнение вообще может быть недоступно, поскольку тип не обязан поддерживать Equatable.
Если требуется реагировать только на реальное изменение, проверку нужно написать явно:
У такого решения есть ограничение: сравнение возможно только для типов, для которых определена подходящая операция сравнения. Для сложных значений также нужно заранее определить, что считать существенным изменением.
Наблюдатели применяются к хранимым свойствам экземпляра и типа, а также могут использоваться поверх унаследованного свойства. Они не превращают свойство в вычисляемое и не позволяют отменить присваивание. Если нужно валидировать или преобразовывать входное значение до сохранения, обычно нужен собственный setter, а не willSet или didSet.
Модель экрана хранит выбранный идентификатор и в didSet запускает загрузку данных. Пользователь повторно выбирает тот же элемент, и загрузка выполняется второй раз. Причина — didSet сообщает о присваивании, а не гарантированно об изменении значения.
Вариант с безусловным действием в didSet прост, но создаёт лишнюю работу. Вариант с проверкой oldValue устраняет повторные операции и обычно является достаточным решением. Вычисляемое свойство с ручным setter даёт больше контроля, включая возможность нормализовать или отклонить значение, но требует самостоятельно реализовать хранение и может быть избыточным для простого сравнения.
Практичный выбор — оставить хранимое свойство с didSet и добавить ранний выход при равенстве старого и нового значения. В результате повторное присваивание остаётся допустимым, но дорогостоящая побочная операция выполняется только при значимом изменении.
willSet и didSet, если новое значение равно старому?Да. Для наблюдаемого хранимого свойства Swift не использует неявную проверку равенства перед вызовом наблюдателей. Если бизнес-логике нужно отфильтровать такие случаи, сравнение с oldValue добавляется явно.
Нет, обычная инициализация свойства наблюдателей не вызывает. В этот момент Swift напрямую устанавливает начальное состояние экземпляра. Наблюдатели начинают работать при последующих присваиваниях уже инициализированному объекту.
willSet изменить записываемое значение или отменить присваивание?Нет. willSet предназначен для реакции перед записью и получает новое значение только для чтения в рамках этого наблюдателя. Он не является валидатором и не может заменить или отменить значение; для преобразования, проверки или отклонения записи нужен собственный setter.