Что меняет объявление сильной ссылки на экземпляр класса через let: сам объект или только возможность перен...

Что меняет объявление сильной ссылки на экземпляр класса через let: сам объект или только возможность переназначить ссылку?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

let запрещает переназначить саму ссылку, но не делает экземпляр класса неизменяемым. Если ссылка сильная, она продолжает удерживать объект живым; его изменяемые свойства можно менять через эту ссылку.

Исторический контекст

В Swift классы используют ссылочную семантику: несколько переменных могут указывать на один экземпляр с общей идентичностью и состоянием. ARC автоматически управляет временем жизни таких экземпляров через подсчёт сильных ссылок, а let и var отдельно определяют, можно ли изменить привязку переменной к объекту.

Такое разделение позволяет запретить замену ссылки, сохранив возможность изменять состояние объекта. Это полезно, когда объект должен оставаться тем же самым экземпляром, но его внутреннее состояние меняется контролируемым API.

Постановка проблемы

Ошибка возникает, когда let трактуют как полную неизменяемость объекта. Тогда кандидат может ошибочно считать, что через константную ссылку нельзя менять свойства класса, либо предположить, что объект перестанет жить после попытки изменить его состояние.

На самом деле нужно различать две операции: изменение самой ссылки и изменение данных экземпляра. Неверное разделение этих понятий приводит к ошибкам в рассуждениях о владении, времени жизни и совместном доступе к объекту.

Подробное решение

Сильная ссылка, объявленная через let, владеет экземпляром так же, как сильная ссылка через var. Пока существует хотя бы одна сильная ссылка, ARC не может уничтожить объект. let лишь запрещает присваивание этой переменной другого экземпляра или значения nil.

У класса состояние хранится внутри объекта, а не в самой ссылочной переменной. Поэтому изменяемое stored-свойство можно менять через let-ссылку, если доступ к нему не ограничен модификаторами доступа и само свойство объявлено как var.

final class Counter { var value = 0 } let counter = Counter() counter.value += 1 // counter = Counter() // Ошибка: ссылку нельзя переназначить

В примере counter остаётся сильным владельцем Counter. Изменение value меняет общий экземпляр, а не ссылку. Если другая сильная ссылка указывает на тот же объект, она увидит это изменение.

Для структуры let действует иначе: её изменяемые свойства нельзя менять через константное значение, потому что изменение свойства меняет само значение структуры. У класса же переменная содержит ссылку, поэтому константность ссылки и изменяемость объекта независимы.

Если требуется запретить изменение состояния объекта, одного let недостаточно. Используют доступ только для чтения, делают свойства private(set) или проектируют класс так, чтобы мутирующие операции были недоступны вызывающему коду.

Ситуация из практики

Контроллер получает сервис и сохраняет его в свойстве let. Разработчик ожидает, что сервис станет неизменяемым, но сервис продолжает менять своё состояние во время работы приложения. Замена let на var не решает проблему: она лишь разрешает заменить сам экземпляр и потенциально усложняет контроль владения.

Варианты решения:

  • оставить сильную ссылку let, если контроллер должен работать с одним и тем же сервисом;
  • скрыть изменяемые свойства сервиса или предоставить только операции чтения, если состояние нельзя менять извне;
  • использовать протокол с доступным только для чтения интерфейсом, если контроллеру не нужны операции изменения.

Обычно выбирают let вместе с ограниченным интерфейсом сервиса. Это сохраняет сильное владение и стабильную идентичность объекта, но не создаёт ложной гарантии неизменяемости его состояния.

Что кандидаты часто упускают

  1. Уменьшает ли let число сильных ссылок по сравнению с var?

Нет. Для ARC оба объявления создают сильную ссылку, если не указан другой квалификатор владения. Разница между ними относится к переназначению ссылки: var можно направить на другой объект, а let — нельзя. Время жизни экземпляра определяется количеством и временем существования сильных ссылок, а не тем, объявлена ли ссылка через let или var.

  1. Можно ли через let изменить объект, если на него указывают несколько ссылок?

Да, если изменяемое свойство доступно. Все ссылки указывают на один экземпляр, поэтому изменение через одну из них видно через остальные. let не создаёт копию и не делает объект уникальным или изолированным от изменений.

  1. Что произойдёт с объектом после выхода let-ссылки из области видимости?

ARC уменьшит число сильных ссылок, когда эта ссылка перестанет существовать, но объект уничтожится только при отсутствии других сильных владельцев. Поэтому выход ссылки из области видимости может вызвать deinit, а может не повлиять на время жизни объекта, если его удерживают свойство, коллекция, замыкание или другая ссылка.