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