Может ли структура сама по себе образовать цикл сильных ссылок, если хранит замыкание?
Нет, структура сама по себе не образует цикл сильных ссылок: это значение, которое копируется, а не объект с собственной идентичностью и временем жизни под управлением ARC. Цикл появляется только тогда, когда замыкание внутри структуры захватывает объект сильной ссылкой, а этот объект прямо или косвенно владеет экземпляром структуры или самим замыканием.
ARC управляет временем жизни экземпляров классов через подсчёт сильных ссылок. Структуры и перечисления имеют семантику значений: при копировании создаётся независимое значение, поэтому ссылки на саму структуру ARC не считает.
Однако замыкания являются ссылочными сущностями. Их контекст захвата может храниться в куче, а захваченные экземпляры классов удерживаются сильными ссылками, если явно не указано иное.
Ошибочно считать, что любое поле структуры автоматически участвует в цикле владения. Например, структура может содержать замыкание, замыкание — захватывать объект, а объект — хранить структуру в свойстве.
В таком случае цикл строится не вокруг структуры как значения, а вокруг объекта, контекста замыкания и хранилища, в котором находится структура. Если не разорвать этот граф, объект не будет уничтожен после обнуления внешней ссылки.
Рассмотрим путь владения: объект Owner сильно владеет структурой Handler; структура владеет замыканием как значением; контекст замыкания сильно захватывает Owner. Последнее звено замыкает цикл: Owner удерживает замыкание, а замыкание удерживает Owner.
После обнуления внешней ссылки Owner не уничтожается: его свойство хранит замыкание, а замыкание сильно удерживает Owner. Сам факт того, что handler имеет тип структуры, цикл не устраняет.
Обычно цикл разрывают слабым захватом объекта. Тогда замыкание не становится владельцем Owner, но вызов может ничего не сделать, если объект уже уничтожен. unowned допустим только при доказуемой гарантии, что замыкание не переживёт объект; иначе обращение приведёт к аварийному завершению.
Ключевой анализ нужно проводить по графу владения: определить, какие узлы являются ссылочными сущностями и какие рёбра являются сильными. Копирование структуры может скопировать замыкание, но это не превращает структуру в отдельный ARC-объект; дополнительные копии замыкания могут лишь добавить сильные удержания захваченных экземпляров.
В экранном состоянии хранят структуру конфигурации с callback-замыканием. Контроллер сохраняет конфигурацию, а callback обращается к контроллеру для обновления интерфейса. После закрытия экрана deinit не вызывается.
Варианты решения:
unowned-захват — не создаёт цикла и не требует optional-проверки, но аварийно завершает программу при ошибочной продолжительности жизни callback.Практически выбирают слабый захват, если callback может быть вызван после уничтожения контроллера. После этого проверяют, что callback не используется для обязательного завершения операции, поскольку объект может исчезнуть раньше вызова.
Нет. Структура не уничтожается ARC как самостоятельный объект. При уничтожении содержащего её объекта освобождаются её поля, включая замыкание; если замыкание удерживает этот же объект, цикл препятствует уничтожению владельца.
Нет. Копирование структуры не гарантирует разрыв графа владения. Если обе копии содержат одно и то же замыкание или эквивалентные замыкания с сильным захватом объекта, каждая копия может сохранять соответствующее сильное удержание.
Нет. Optional меняет возможность отсутствия значения, но не тип владения содержащегося значения. Если optional содержит замыкание, само замыкание по-прежнему может сильно удерживать объект. Цикл исчезнет только после удаления сильного звена, например при присваивании nil или использовании слабого захвата.