Какой уровень состояния делает изменяемым передача переменной, содержащей ссылку на класс, через inout?
inout делает изменяемой саму переменную-ссылку: через неё можно переназначить ссылку на другой объект. Состояние объекта можно менять и без inout, если ссылка доступна для вызова его изменяющих методов или свойств.
Swift сочетает value semantics для структур и перечислений с reference semantics для классов. Механизм inout нужен для явной передачи переменной на изменение, сохраняя при этом контроль над эксклюзивным доступом к её хранилищу.
Такой дизайн отделяет изменение значения переменной от изменения объекта, на который она ссылается. Это помогает не скрывать потенциальную замену значения и уменьшает неоднозначность при работе с памятью.
Переменная типа класса хранит не сам объект, а ссылку на него. Поэтому нужно различать две операции: изменение свойств существующего объекта и переназначение переменной на другой объект.
Без inout функция может изменить свойства доступного объекта, но не может заменить ссылку вызывающей стороны. Ошибочное смешение этих операций приводит к неверному ожиданию, что передача ссылки позволит функции изменить исходную переменную.
inout предоставляет функции доступ к хранилищу переменной по семантике «копирование при входе и запись результата при выходе». Конкретная реализация может оптимизировать копирование, но код должен рассуждать так, будто значение временно передаётся функции, а затем возвращается обратно.
Если значение переменной — ссылка на класс, inout относится к контейнеру, в котором хранится ссылка. Поэтому функция может присвоить этому контейнеру ссылку на новый объект, и после возврата вызывающая переменная будет указывать уже на него.
При этом вызов метода или изменение свойства через inout-параметр может менять тот же объект, на который ссылка указывала изначально. Однако это изменение объекта не является особым свойством inout: аналогичная операция обычно возможна через обычный параметр класса.
После вызова replace переменная box ссылается на новый объект, а old сохраняет ссылку на прежний. Сам объект не копируется автоматически: меняется именно ссылка в переменной.
Переменная, передаваемая через inout, должна быть доступна для изменения, поэтому константу передать таким способом нельзя. Во время действия inout Swift также контролирует эксклюзивность доступа: одновременное конфликтующее чтение и изменение одного хранилища запрещено.
Предположим, функция обновляет состояние объекта и иногда должна заменить его реализацию. Обычный параметр класса подходит для изменения свойств, но присваивание параметру нового объекта не изменит переменную вызывающего кода.
Можно вернуть новый объект из функции. Это явно выражает замену и часто проще для тестирования, но требует обработать возвращаемое значение. Можно передать переменную через inout: тогда замена ссылки видна вызывающей стороне, однако появляется требование изменяемой переменной и более строгий контроль доступа.
Для операции, которая действительно должна заменить объект в вызывающем коде, выбирают inout. Если требуется только изменить состояние существующего экземпляра, предпочтительнее обычный параметр класса: он точнее передаёт намерение и не создаёт лишнего доступа к хранилищу ссылки.
Меняет ли inout объект или ссылку, если функция изменяет свойство класса?
Вызов свойства меняет объект, на который указывает ссылка. Само значение переменной-ссылки при этом может остаться прежним. inout в таком случае не является причиной reference semantics — он лишь предоставляет функции изменяемый доступ к переменной.
Можно ли через обычный параметр класса заменить объект вызывающей стороны?
Нет. Переназначение параметра меняет только локальную переменную внутри функции. Поскольку параметр получает копию ссылки, обе ссылки сначала указывают на один объект, но последующее присваивание параметру не переписывает ссылку у вызывающего кода.
Гарантирует ли inout постоянную ссылку на объект?
Нет. inout не фиксирует объект и не запрещает переназначение переменной. Он предоставляет временный эксклюзивный доступ к её хранилищу, поэтому за время вызова ссылка может быть заменена, а после завершения функции новое значение записывается обратно.