Почему willSet/didSet не срабатывают при присваивании внутри init и как это обойти?
Счётчик ниже логирует каждое изменение через didSet, но значение, переданное в init, никогда не логируется. Объясните, почему наблюдатель пропускается, затем исправьте класс так, чтобы эффект отработал и для начального значения.
Ограничения:
- Сохраните наблюдатель
didSetдля последующих записей. - Не меняйте места вызова, создающие
Counter. - Лог должен сработать один раз и для инициализирующего значения.
final class Counter {
var value: Int = 0 {
didSet { print("value changed to \(value)") }
}
init(start: Int) {
self.value = start // didSet тут НЕ печатает
}
}
Найдите и исправьте ошибку.
Внутри init объект ещё не полностью инициализирован, поэтому первое присваивание ставит значение, и Swift пропускает наблюдатели. Обходят это, запуская эффект после init вручную, либо через обёртку свойства, чей setter выполняется всегда.
- ✗Ждут, что побочный эффект
didSetотработает для значения, присвоенного вinit - ✗Винят «вырезание» наблюдателей из-за наличия собственного инициализатора
- ✗Берут
lazyвместо явного запуска эффекта после инициализации
- →Срабатывают ли наблюдатели, если свойство меняет метод, вызванный из
init? - →Почему setter обёртки свойства срабатывает при инициализирующем присваивании?
Наблюдатель пропущен, потому что внутри init объект ещё не полностью инициализирован: первое присваивание хранимого свойства устанавливает значение, а не «меняет» его, поэтому willSet/didSet не вызываются. Самый прямой обход — вынести эффект в метод и вызвать его после присваивания в init:
final class Counter {
var value: Int = 0 {
didSet { log() }
}
init(start: Int) {
self.value = start
log() // запускаем эффект, который наблюдатель пропустил
}
private func log() { print("value changed to \(value)") }
}
Альтернатива — инкапсулировать правило в обёртку свойства: её wrappedValue-setter выполняется и при инициализирующем присваивании, поэтому эффект нельзя пропустить, и места вызова остаются прежними.