Сравните поведение слабой и невладеющей ссылки при обращении после уничтожения объекта.
Слабая ссылка автоматически становится nil после уничтожения объекта, поэтому обращение к ней безопасно, если обработать отсутствие значения. Невладеющая ссылка не обнуляется; обращение к ней после уничтожения объекта приводит к аварийному завершению программы.
Обе ссылки не увеличивают счётчик сильных ссылок, но предъявляют разные требования к времени жизни объекта: для unowned оно должно быть гарантировано дольше, чем время использования ссылки.
ARC автоматизирует управление подсчётом ссылок: компилятор добавляет операции удержания и освобождения объектов вместо ручного управления памятью. Однако автоматический подсчёт не устраняет циклы сильных ссылок, поэтому в Swift нужны ссылки, которые не владеют объектом.
weak и unowned решают эту задачу по-разному. weak ориентирована на безопасное наблюдение за объектом с потенциально независимым временем жизни, а unowned — на связь, в которой время жизни объекта гарантированно покрывает время использования ссылки.
Представим объект, который хранит ссылку на другой объект, но не должен продлевать его жизнь. После уничтожения объекта-ссылки прежнее значение может стать недействительным.
Если использовать weak, ARC обнулит ссылку, и программа сможет проверить отсутствие объекта. Если использовать unowned, ссылка сохранит адресную связь без владения, но обращение к уже уничтоженному объекту завершится ошибкой времени выполнения.
Неверный выбор unowned особенно опасен в асинхронном коде: задача, таймер или замыкание могут жить дольше экрана или другого объекта, который считался «гарантированно существующим».
weak хранится без сильного владения и имеет optional-тип. Когда объект уничтожается, ARC обнуляет все weak-ссылки на него. Поэтому чтение такой ссылки после уничтожения возвращает nil, но доступ требует безопасной распаковки или проверки.
unowned также не увеличивает число сильных ссылок, но не обнуляется. Обычная unowned-ссылка сохраняет информацию о том, на какой объект она указывала; если объект уже уничтожен, проверяемый runtime-доступ к ссылке вызывает trap. Это не способ «попробовать обратиться», а утверждение о гарантии времени жизни.
В примере weakReference становится nil, когда локальная сильная ссылка session исчезает. Доступ к requiredSession после уничтожения переданного Session был бы ошибкой времени выполнения.
Выбор обычно определяется гарантией времени жизни:
weak — когда объект может исчезнуть раньше ссылки или это зависит от внешнего управления; цена — optional-проверки и дополнительная работа runtime для обнуления.unowned — когда жизненный контракт строгий и доказуемый; код не требует optional-доступа, но нарушение контракта вызывает аварийное завершение.unowned(unsafe) — ещё более рискованный вариант без проверки жизнеспособности ссылки; после уничтожения объекта поведение небезопасно, поэтому применять его следует только при очень строгом контроле времени жизни.ARC освобождает объект, когда исчезает последняя сильная ссылка. Слабые и невладеющие ссылки этому не препятствуют. При этом не следует считать границей жизни объекта исключительно конец видимой области кода: компилятор может освободить сильную ссылку после последнего необходимого использования.
Экран запускает длительную операцию, а объект операции хранит замыкание для уведомления экрана о результате. Пользователь закрывает экран до завершения операции. Операция и её замыкание могут продолжить жить независимо от экрана.
Вариант с unowned предполагает, что экран гарантированно переживёт операцию. Его плюс — отсутствие optional-проверок, но при позднем завершении операции приложение аварийно завершится при обращении к экрану.
Вариант с weak захватывает экран без владения. Его минус — результат может быть проигнорирован, если экран уже уничтожен, зато приложение не падает и не удерживает экран до окончания операции.
В такой ситуации выбирают weak, если закрытие экрана действительно может предшествовать завершению операции. Замыкание проверяет наличие экрана и обновляет интерфейс только при существующем объекте; результатом становится безопасное прекращение обновления без утечки и аварийного завершения.
Вопрос: может ли weak-ссылка продлить время жизни объекта косвенно?
Нет. weak не является владельцем и не увеличивает счётчик сильных ссылок. Однако временное получение объекта из weak-ссылки в локальную сильную переменную может удержать объект на время этой операции; это обычное кратковременное сильное владение, а не свойство самой weak-ссылки.
Вопрос: достаточно ли объявить ссылку как unowned, чтобы цикл ссылок исчез?
Нет. Цикл исчезает только в том случае, если именно одна из связей в цикле перестаёт быть сильной. unowned действительно не удерживает объект, но если выбранная связь не участвует в цикле или другая цепочка сильных ссылок сохраняет объект, проблема не решается. Кроме того, unowned может превратить прежнюю утечку в аварийное завершение при неверном жизненном контракте.
Вопрос: чем отличается обычная unowned от unowned(unsafe) после уничтожения объекта?
Обычная unowned-ссылка проверяется runtime и при обращении к уничтоженному объекту приводит к контролируемому trap. unowned(unsafe) отключает эту проверку; обращение после уничтожения объекта небезопасно и не должно использоваться как штатный способ оптимизации. Такой вариант допустим только при доказуемой гарантии, что ссылка никогда не будет прочитана после завершения жизни объекта.