Представьте, что ссылка на экземпляр класса объявлена через let: за счёт чего состояние объекта всё ещё можно изменить?
let делает неизменяемой саму ссылку, но не объект, на который она указывает. Поэтому ссылку нельзя переназначить на другой экземпляр, однако изменяемые свойства этого экземпляра можно менять.
В Swift одновременно существуют семантика значений и семантика ссылок. Значимые типы обычно рассматриваются как независимые значения, а экземпляры классов — как объекты с идентичностью, к которым могут обращаться несколько ссылок.
Разделение позволяет отдельно контролировать изменение привязки и изменение объекта. Это решает разные задачи: let защищает связь имени с экземпляром, а модификаторы свойств и архитектура типа определяют, можно ли менять внутреннее состояние.
Разработчик может ошибочно считать, что let делает полностью неизменяемым всё, что с ним связано. В случае класса это приводит к ложному ощущению потокобезопасности или неизменяемости общего состояния.
Если несколько частей программы имеют ссылки на один объект, изменение его свойства будет наблюдаться через все эти ссылки. При этом попытка присвоить самой переменной другой экземпляр завершится ошибкой компиляции.
Переменная типа класса хранит ссылочное значение — идентификатор или ссылку на объект. Объявление через let запрещает изменить это значение ссылки после инициализации, но не накладывает автоматически запрет на мутирующие операции самого объекта.
В примере settings по-прежнему указывает на тот же экземпляр, поэтому его свойство theme изменяется. final здесь запрещает наследование класса, но не делает его экземпляры неизменяемыми.
Чтобы запретить изменение состояния через конкретный интерфейс, применяют private(set), свойства только для чтения, неизменяемые типы-значения или предоставляют операции, явно контролирующие изменение. Для конкурентного доступа одной декларации let недостаточно: она не обеспечивает синхронизацию и потокобезопасность.
Команда хранит общие настройки приложения в экземпляре класса и передаёт его в несколько сервисов через let. Разработчик считает настройки защищёнными от изменений, но один сервис меняет публичное свойство, после чего поведение других сервисов меняется неожиданно.
Вариант с оставлением публичных изменяемых свойств прост, но не контролирует источник изменений. Вариант с let у всех ссылок предотвращает только замену самого объекта и не решает проблему общего изменяемого состояния.
Более надёжное решение — закрыть запись, например через private(set), и предоставить методы или операции, проверяющие допустимость изменений. Если настройки должны быть снимком состояния, лучше передавать структуру и заменять её целиком; если состояние разделяется между конкурентными задачами, нужен согласованный механизм изоляции, например actor.
Результат зависит от требований: для локального владения достаточно ограничить доступ к свойствам, для общего состояния важны также контроль API и синхронизация. let остаётся полезной гарантией неизменности привязки, но не заменяет эти меры.
let?let запрещает заменить ссылку на другой объект, но не запрещает изменять состояние объекта, на который она указывает. Например, постоянное свойство класса может всегда ссылаться на один экземпляр другого класса, а его изменяемые свойства всё равно останутся доступными. Если требуется запретить и такую мутацию, сам вложенный тип или его интерфейс тоже должен обеспечивать неизменяемость.
let объект неизменяемым, если на него больше нет других ссылок?Нет. Количество ссылок не меняет правило семантики. Пока объект доступен через единственную let-ссылку, его изменяемые свойства можно менять; отсутствие других ссылок влияет только на совместное наблюдение изменений, но не на возможность мутации.
final поведение let для экземпляра класса?Нет. final запрещает наследование класса и переопределение его членов в подклассах, но не превращает свойства экземпляра в константы. Неизменность состояния нужно задавать отдельно: например, ограничивать запись, использовать свойства только для чтения или проектировать тип без операций изменения.