Разберите, почему изменение структуры после присваивания не меняет исходное значение в Swift.
Структуры в Swift имеют value semantics: после присваивания переменная получает самостоятельное значение, поэтому изменение одной переменной не изменяет другую. Компилятор и стандартная библиотека могут оптимизировать физическое копирование, но наблюдаемое поведение сохраняет независимость значений.
Подход с семантикой значений решает проблему неявного совместного владения изменяемым состоянием. Если несколько частей программы работают с одним изменяемым объектом, изменение через одну ссылку может неожиданно повлиять на остальные части.
В Swift структуры являются основным инструментом моделирования данных, а классы предназначены для случаев, где нужна идентичность объекта и совместно изменяемое состояние. Такое разделение делает последствия присваивания и передачи параметров более предсказуемыми.
Представим, что значение конфигурации или модели передали в другой компонент, а затем этот компонент изменил его. При семантике значений исходная модель не должна внезапно измениться через побочный эффект присваивания.
Неверно считать, что любая передача переменной означает физическое копирование всех байтов. Важно не физическое устройство памяти, а контракт: независимые значения не должны наблюдать изменения друг друга. Если перепутать структуру с классом, можно получить либо неожиданные изменения общего состояния, либо лишнее копирование и усложнение синхронизации.
При присваивании структуры копируется ее значение. После этого изменение хранимого свойства одной переменной относится только к этой переменной:
Для структуры это гарантируется семантикой значений. В отличие от нее, экземпляр класса имеет семантику ссылок: присваивание другой переменной обычно передает ссылку на тот же объект, поэтому изменение через одну переменную видно через другую.
Физическая копия может быть отложена. Например, Array, String и некоторые другие типы используют copy-on-write: несколько значений временно могут разделять внутреннее хранилище, пока никто его не изменяет. При попытке изменения хранилище отделяется, если оно совместно используется. Это оптимизация реализации, а не изменение семантики.
Семантика значений не означает полной рекурсивной независимости всех вложенных данных. Структура может содержать свойство ссылочного типа, например экземпляр класса. Тогда копируются поля структуры, но две копии могут сохранить ссылку на один и тот же вложенный объект.
Также важно различать let и var. let запрещает изменение самого значения через данное имя, но не превращает вложенный ссылочный объект в неизменяемый. Для структуры let запрещает изменение ее изменяемых свойств через эту привязку; для let-ссылки на класс можно изменять изменяемые свойства объекта.
Сервис загрузки профиля получает модель пользователя и передает ее в экран редактирования. Экран должен менять черновик, не затрагивая подтвержденные данные до нажатия кнопки сохранения.
Первый вариант — сделать модель классом. Его плюс — явная общая идентичность и удобное совместное наблюдение изменений. Минус — экран может случайно изменить исходную модель до подтверждения, а поиск таких побочных эффектов становится сложнее.
Второй вариант — использовать структуру и передавать ее в редактор как отдельное значение. Это дает изоляцию черновика и понятное поведение при отмене редактирования; при этом большие коллекции не обязательно сразу физически копируются благодаря copy-on-write. Цена решения — изменения нужно явно вернуть или сохранить, а для действительно общего состояния структура сама по себе не подходит.
Выбран вариант со структурой: подтвержденный профиль остается неизменным, пока редактор не сформирует новое значение. В результате отмена редактирования не требует отката скрыто измененного объекта, а сохранение становится явной операцией передачи нового значения.
1. Всегда ли присваивание структуры немедленно копирует все данные в памяти?
Нет. Семантически значения независимы, но реализация может использовать оптимизацию copy-on-write. Например, два массива могут временно использовать одно внутреннее хранилище, а отдельная копия создается только при изменении одного из них. Поэтому нельзя делать вывод о производительности только из факта присваивания структуры.
2. Станут ли две копии структуры полностью независимыми, если одно из ее свойств является классом?
Нет. Копируется сама структура и значение ее свойства-ссылки, но ссылка может указывать на тот же экземпляр класса. В таком случае изменение состояния вложенного объекта будет видно из обеих структур. Для полной независимости требуется явно создать независимую копию вложенного объекта или заменить его на тип со значимой семантикой.
3. Чем отличается передача структуры от передачи класса в функцию с точки зрения изменения данных?
Структура передается как значение: изменение локальной копии параметра не меняет исходную переменную, если не используется специальная передача с inout. Экземпляр класса передается как значение ссылки: локальный параметр и исходная переменная могут обозначать один объект, поэтому изменение его свойств обычно наблюдается снаружи. При этом переназначение локального параметра класса не переназначает исходную переменную — общим остается объект, а не сами привязки.