Какое ограничение Swift не позволяет добавить хранимое свойство в extension уже объявленного типа?
extension не может добавлять хранимые свойства экземпляра или типа. В нём можно объявлять вычисляемые свойства, методы, инициализаторы, вложенные типы и соответствия протоколам, но новое состояние должно быть размещено в основном объявлении типа.
Причина в том, что хранимое свойство изменяет модель хранения и правила инициализации экземпляра. На момент формирования типа Swift должен знать, где находится это состояние и каким образом оно получает начальное значение.
Механизм extension появился как способ разделять реализацию типа по файлам и логическим блокам, не изменяя исходное объявление. Это особенно полезно для группировки методов, реализации протоколов и добавления функциональности к типам из других модулей.
Хранимые свойства требуют другого уровня изменений: они участвуют в размещении экземпляра, инициализации и управлении его состоянием. Поэтому extension расширяет поведение типа, но не его хранилище.
Представьте, что к уже используемому типу нужно добавить новое состояние: например, к модели требуется кэш вычисленного значения. Если попытаться объявить в extension обычное хранимое свойство, компилятор отклонит код.
Если бы такое добавление было разрешено без ограничений, пришлось бы согласовать новое поле с размером экземпляра, инициализаторами, правилами доступа и двоичной совместимостью. Кроме того, экземпляры, созданные до появления extension, должны были бы иметь корректное значение нового состояния.
Хранимое свойство владеет памятью, в которой непосредственно содержится значение. Поэтому его объявляют в основном типе и учитывают при инициализации всех экземпляров. Вычисляемое свойство памяти для собственного значения не добавляет: оно вычисляет результат через уже существующее состояние, поэтому его можно объявить в extension.
Например, extension может предоставить производное свойство:
Здесь fullName не хранит строку отдельно, а вычисляет её при обращении. Если требуется именно изменяемое состояние, варианты такие:
Последний вариант не является универсальной заменой хранимому свойству: он применим не ко всем Swift-типам, усложняет владение и отладку и обычно уступает явному полю. Property wrapper тоже не обходят правило: его backing storage должно быть создано там, где разрешено объявлять хранимое свойство.
Команде нужно добавить к публичной модели User кэш форматированного имени, не меняя основной файл типа. Рассматривались вычисляемое свойство, внешний словарь и associated objects.
Вычисляемое свойство проще и безопаснее, но не кэширует результат. Внешний словарь может хранить кэш, однако требует ключей, синхронизации и очистки, а также может удерживать объекты дольше ожидаемого. Associated objects позволяют привязать состояние к экземпляру класса, но зависят от Objective-C runtime и не подходят для структур.
Если вычисление дешёвое, выбирают вычисляемое свойство в extension. Если кэш действительно необходим, лучше изменить основной тип и явно добавить туда хранилище; это делает владение, жизненный цикл и потокобезопасность понятными.
Можно ли добавить в extension вычисляемое свойство с get и set?
Да, если чтение и запись используют уже существующее состояние, например другие свойства или внешний источник данных. Такое свойство не получает собственного хранилища. Если же setter должен сохранять новое значение в отдельном поле, одного extension недостаточно.
Можно ли добавить хранимое свойство в extension подкласса?
Нет. Ограничение относится и к extension базового класса, и к extension подкласса. Новое поле нужно объявить непосредственно в теле класса подкласса, чтобы оно участвовало в его инициализации и модели хранения.
Как реализовать в extension требование протокола, которое выглядит как свойство?
Требование можно удовлетворить вычисляемым свойством, если значение выводится из доступного состояния. Например, протокол может требовать свойство isEmpty, а extension реализует его через существующий размер коллекции. Если протоколу нужно независимое изменяемое состояние, его необходимо хранить в основном типе либо использовать внешний механизм хранения с соответствующими компромиссами.