Что изменится для автоматически созданного поименованного инициализатора структуры после добавления собственного инициализатора в её основное объявление?
Если добавить собственный инициализатор непосредственно в объявление структуры, Swift перестанет синтезировать для неё стандартный memberwise initializer. Вызовы, которые передавали значения всех хранимых свойств по именованным параметрам, больше не скомпилируются, пока такой инициализатор не будет объявлен явно.
Memberwise initializer появился как часть удобной модели структур-значений: компилятор может автоматически создать инициализатор, который принимает хранимые свойства структуры. Это уменьшает шаблонный код для простых моделей данных и позволяет сосредоточиться на поведении типа.
Одновременно Swift должен отличать простую структуру-данные от типа с собственной логикой создания. Поэтому наличие пользовательского инициализатора в основном объявлении считается сигналом, что правила построения экземпляра контролирует разработчик.
После добавления собственного инициализатора прежние места создания структуры могут перестать компилироваться. Особенно опасно это при рефакторинге модели: изменение одного объявления типа способно сломать код, который использовал автоматически созданный инициализатор.
Кроме того, автоматически созданный инициализатор обычно имеет внутреннюю доступность и не становится публичным только потому, что сама структура объявлена как public. Если структура используется из другого модуля, внешний код не сможет полагаться на такой инициализатор как на публичный API.
Swift синтезирует memberwise initializer для структуры при соблюдении условий, в частности когда в основном объявлении структуры нет пользовательского инициализатора. Его параметры соответствуют хранимым свойствам; для свойств со значениями по умолчанию соответствующие параметры также могут иметь значения по умолчанию.
Если собственный инициализатор объявлен в основном теле структуры, автоматический memberwise initializer подавляется. Это не перегрузка автоматически созданного инициализатора, а изменение набора синтезируемых возможностей типа.
Инициализатор, добавленный в расширении, имеет важное отличие: он не подавляет memberwise initializer, созданный для основного объявления структуры. Поэтому расширение можно использовать для добавления альтернативных способов создания, сохранив синтезированный инициализатор для внутренних нужд.
В этом примере расширение добавляет собственный инициализатор, но вызов Point(x:y:) остаётся доступным. Если перенести init(onAxis:) в основное объявление структуры, memberwise initializer больше не будет синтезирован и вызов Point(x:y:) потребует явного восстановления.
Явный инициализатор предпочтительнее, когда нужно стабилизировать публичный API, проверить инварианты или скрыть внутреннее устройство модели. Сохранение memberwise initializer удобнее для простых DTO и тестовых моделей, но связывает вызывающий код с набором их хранимых свойств.
Команда добавляет в структуру конфигурации инициализатор, принимающий строковое значение и преобразующий его в несколько свойств. После этого тесты, создававшие конфигурацию через параметры отдельных свойств, перестают собираться.
Можно явно восстановить memberwise initializer в основном объявлении. Это сохраняет совместимость, но увеличивает код и требует обновлять инициализатор при добавлении или изменении свойств.
Можно перенести новый инициализатор в расширение. Тогда memberwise initializer продолжит синтезироваться, но такой вариант подходит только если прямое создание объекта из отдельных свойств не нарушает инварианты.
В модели с обязательной валидацией выбран бы явный публичный инициализатор с проверками, а memberwise initializer оставили бы недоступным для внешнего кода. В простой внутренней структуре без сложных правил создания практичнее расширение: оно сохраняет краткость и уменьшает риск лишнего шаблонного кода.
1. Сохраняется ли memberwise initializer, если пользовательский инициализатор добавить в расширении?
Да, для структуры это позволяет сохранить автоматически созданный memberwise initializer. Ограничение относится к пользовательскому инициализатору в основном объявлении структуры, а не к инициализатору в расширении. Это распространённый способ разделить базовое описание данных и дополнительные способы создания.
2. Станет ли автоматически созданный memberwise initializer публичным у публичной структуры?
Нет, на автоматическую публичность рассчитывать нельзя. Синтезированный memberwise initializer обычно имеет внутреннюю доступность, поэтому код другого модуля не должен использовать его как публичный конструктор. Если он является частью внешнего API, его следует объявить явно с нужным уровнем доступа.
3. Как добавление нового хранимого свойства влияет на код, использующий memberwise initializer?
Сигнатура синтезированного инициализатора может измениться, поскольку она отражает хранимые свойства структуры. Это может привести к ошибкам сборки или изменить доступные значения по умолчанию. Явный инициализатор изолирует вызывающий код от таких изменений, но разработчику нужно самостоятельно решить, как инициализировать новое свойство.