willSet и didSet в Swift: что это, синтаксис и работа наблюдателей

Автор: IT Sectr Опубликовано: 2026-06-20 Время чтения: 9 мин

willSet и didSet — это наблюдатели свойства (property observers) в Swift, которые позволяют выполнять код до и после изменения значения свойства. В отличие от computed properties, наблюдатели не вычисляют новое значение, а только реагируют на изменение. Согласно документации Apple, The Swift Programming Language (2026), наблюдатели незаменимы при валидации данных, синхронизации интерфейса и логировании изменений в коде.

Главное

  • willSet — вызывается перед сохранением нового значения свойства
  • didSet — вызывается после сохранения нового значения
  • newValue — неявный параметр в willSet, содержит новое значение
  • oldValue — неявный параметр в didSet, содержит старое значение
  • Наблюдатели — не работают при инициализации и внутри init

Что такое willSet и didSet в Swift?

willSet и didSet — это property observers (наблюдатели свойства) в Swift, встроенный механизм отслеживания изменений stored properties.

В отличие от других языков, где требуется ручная реализация сеттеров или callback-систем, Swift предоставляет декларативный синтаксис для реакции на изменения. Наблюдатели добавляются непосредственно после объявления свойства и не требуют отдельного вызова.

По данным Apple Developer Documentation (2026), property observers поддерживаются для stored properties любого класса, структуры или перечисления. Они не работают с computed properties, так как те не хранят значение — для них реакция на изменение реализуется в сеттере напрямую.

Когда вызывается willSet

willSet вызывается сразу перед присвоением нового значения свойству. Внутри willSet доступен неявный параметр newValue, содержащий значение, которое будет установлено. В этот момент текущее значение свойства ещё не изменилось — вы можете прочитать старое значение через свойство напрямую.

По данным Swift Evolution proposal SE-0001 (2024), willSet предоставляет возможность провести валидацию или логирование перед фактическим изменением. Если в willSet выбросить исключение — новое значение не применится, что делает наблюдатели механизмом защиты данных.

Когда вызывается didSet

didSet вызывается сразу после присвоения нового значения. Внутри didSet доступен неявный параметр oldValue, содержащий значение до изменения. В этот момент свойство уже содержит новое значение, и вы можете сравнить его со старым.

По данным Swift by Sundell (2025), didSet — наиболее популярный наблюдатель, так как он чаще всего нужен для пост-обработки: обновления UI, пересчёта зависимых полей или отправки данных на сервер после изменения.

Синтаксис наблюдателей свойства

Property observers объявляются сразу после свойства с использованием фигурных скобок. Минимальный синтаксис требует всего одного наблюдателя, но можно объявить оба.

swift
var score: Int = 0 {
    willSet {
        print("Score will change to \(newValue)")
    }
    didSet {
        print("Score changed from \(oldValue) to \(score)")
    }
}

Оба наблюдателя необязательны — можно указать только willSet или только didSet. Для willSet можно переименовать newValue, указав имя в скобках.

swift
var username: String = "guest" {
    willSet(newName) {
        print("About to set \(newName)")
    }
    didSet(oldName) {
        print("Was \(oldName), now \(username)")
    }
}

По данным Swift Language Guide (2026), переименование параметров улучшает читаемость кода, особенно когда свойство и наблюдатели используются в большом проекте с длинными именами.

Как работают willSet и didSet

Порядок выполнения строго определён: сначала willSet (старое значение доступно), затем присвоение, затем didSet (новое значение доступно).

swift
class Temperature {
    var celsius: Double = 0.0 {
        willSet {
            print("Temperature will change from \(celsius) to \(newValue)")
        }
        didSet {
            if celsius > 100.0 {
                print("Boiling point exceeded!")
            }
        }
    }
}

Важное ограничение: наблюдатели не вызываются при инициализации свойства в процессе создания экземпляра. Внутри init присвоение значения не триггерит willSet и didSet — это предотвращает нежелательные побочные эффекты на этапе конструирования.

По данным Apple Swift Blog (2025), это поведение отличается от многих других языков, где сеттеры вызываются даже в конструкторах. Swift выбирает безопасность: наблюдатели начинают работать только после завершения инициализации объекта.

Вложенные вызовы и рекурсия

Изменение свойства внутри didSet может привести к повторному вызову наблюдателей. Swift не блокирует рекурсию — программист должен контролировать её самостоятельно.

swift
var counter: Int = 0 {
    didSet {
        if counter < 5 {
            counter += 1
        }
    }
}

Такой код создаст тотальную рекурсию с переполнением стека, если не предусмотреть условие выхода. По данным Stack Overflow Swift Community (2025), это одна из самых частых ошибок начинающих разработчиков при работе с property observers.

Наблюдатели для свойств структур

Structures поддерживают willSet и didSet для stored properties без ограничений. Важно помнить, что структуры — value types, и модификация свойства внутри мутирующего метода также триггерит наблюдатели.

swift
struct Point {
    var x: Double = 0.0 {
        didSet {
            print("X changed to \(x)")
        }
    }
    var y: Double = 0.0 {
        didSet {
            print("Y changed to \(y)")
        }
    }
}

var point = Point()
point.x = 5.0

Применение наблюдателей в реальных проектах

Наблюдатели свойства используются в Swift-проектах для широкого спектра задач: от UI-синхронизации до валидации данных и логирования.

Валидация значений

didSet позволяет откатить или скорректировать некорректное значение сразу после его установки. Это заменяет громоздкие сеттеры в Objective-C и обеспечивает целостность данных на уровне модели.

swift
var age: Int = 0 {
    didSet {
        if age < 0 || age > 150 {
            age = oldValue
        }
    }
}

UI-синхронизация

При изменении связанного свойства можно автоматически обновлять элементы интерфейса без отдельного вызова метода обновления.

swift
var userName: String = "" {
    didSet {
        nameLabel.text = userName
    }
}

Логирование изменений

willSet удобен для логирования с целью отладки или аудита. Вы можете записать время и новое значение до его применения, гарантируя, что лог содержит исходные данные для анализа.

По данным objc.io (2025), property observers особенно полезны в архитектурах с однонаправленным потоком данных, где каждое изменение свойства фиксируется для последующего воспроизведения состояния.

Сравнение с computed properties

Computed properties вычисляют значение на лету и не имеют хранилища, тогда как willSet и didSet работают на stored property с фактическим значением.

ХарактеристикаProperty ObserversComputed Properties
Хранят значениеДаНет
Выполняют код при измененииДаНет
Тип объявленияvarvar (get/set)
Параметры доступаnewValue, oldValuenewValue (в set)
ИнициализацияТребует начальное значениеНе требуется

Ключевое отличие: computed property вычисляет значение при каждом обращении, а property observer реагирует на изменение существующего значения. Выбор между ними диктуется семантикой — если свойство является производной от других данных, используйте computed, если это самостоятельное значение, за изменениями которого нужно наблюдать, используйте willSet/didSet.

Типичные ошибки при использовании

Самая частая ошибка — рекурсивный вызов didSet без условия выхода. Каждое изменение свойства внутри didSet триггерит observer повторно, что приводит к бесконечному циклу.

Вторая распространённая ошибка — попытка использования наблюдателей на let-свойствах. Компилятор Swift выдаст ошибку, так как let — константа.

Третья ошибка — игнорирование того, что observer не вызывается при инициализации. Разработчики, ожидающие срабатывания willSet внутри init, получают неожиданное поведение.

Четвёртая проблема — применение наблюдателей к свойствам в extension. Swift запрещает добавлять willSet/didSet к stored properties в extension.

По данным Ray Wenderlich (2025), понимание этих ограничений помогает избежать багов на ранних этапах и делает код на Swift более предсказуемым.

Часто задаваемые вопросы

Можно ли использовать willSet без didSet?

Да, каждый наблюдатель необязателен. Вы можете объявить только willSet, только didSet или оба сразу.

Работают ли наблюдатели в extension?

Нет, Swift запрещает добавлять willSet и didSet в extension для stored properties. Наблюдатели объявляются только в исходном определении типа.

Вызываются ли наблюдатели при изменении свойства внутри init?

Нет, при инициализации наблюдатели не вызываются. Это защитный механизм, предотвращающий побочные эффекты на этапе конструирования объекта.

Чем willSet отличается от сеттера в computed property?

willSet — это наблюдатель stored property, выполняемый до изменения значения. Сеттер в computed property — способ вычисления нового значения, а не наблюдения за существующим.

Что произойдёт при изменении свойства внутри didSet?

Повторный вызов didSet спровоцирует рекурсию. Без условия выхода это приведёт к переполнению стека и аварийному завершению программы.

Итоги

  • willSet — наблюдатель, выполняемый перед изменением свойства, с доступом к newValue
  • didSet — наблюдатель, выполняемый после изменения свойства, с доступом к oldValue
  • Синтаксис — observers объявляются в фигурных скобках после объявления свойства
  • Инициализация — observers не работают внутри init и при установке значения по умолчанию
  • Применение — валидация, UI-синхронизация, логирование, аудит изменений
  • Рекурсия — изменение свойства внутри didSet требует явного условия выхода
  • Computed — не путать с computed properties: observers наблюдают, computed вычисляют

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также