Зачем нужен unowned, если есть weak
Что проверяют
Понимаете ли вы ARC глубже, чем «weak спасает от циклов ссылок». Следом обычно просят показать, где unowned оправдан, и спрашивают, что будет при обращении к unowned-ссылке после освобождения объекта.
Как отвечать
Обе ссылки не увеличивают сильный счётчик и разрывают цикл удержания. Разница в том, что они обещают:
weak: объект может исчезнуть в любой момент. Ссылка всегда опциональная и после освобождения объекта сама становитсяnil. Цена: каждое обращение надо разворачивать, а у объекта заводится отдельная side table для слабых ссылок;unowned: вы гарантируете, что объект переживёт ссылку или умрёт вместе с её владельцем. Ссылку можно объявить неопциональной и дажеlet. Обращение после освобождения объекта даёт неnil, а мгновенный крэш;unowned(unsafe): без проверки вовсе, обращение к мёртвому объекту это неопределённое поведение. Нужен редко, в горячем коде, где проверка доказанно мешает.
Зачем unowned, если weak безопаснее: он точно выражает модель владения. Классический пример из документации Swift: у Customer есть опциональная карта, а CreditCard без владельца не существует, поэтому unowned let customer: Customer. Опциональность здесь была бы ложью в типе.
Второй случай: замыкание, которое хранит сам объект и которое не вызывается после его смерти, например внутренний обработчик или ленивое свойство. Там [unowned self] честнее, чем [weak self] с guard.
Деталь для сильного ответа: пока на объект есть unowned-ссылки, его deinit уже отработал, но память под экземпляр не освобождается до обнуления счётчика unowned. Поэтому крэш детерминированный, а не случайное чтение чужой памяти.
Где спотыкаются
Ставят [unowned self] в замыкание сетевого запроса или анимации. Экран закрыли, ответ пришёл позже, приложение упало.
Считают, что unowned быстрее и поэтому лучше. Выигрыш есть, но в прикладном коде он незаметен, а риск крэша вполне реальный.
С [weak self] пишут self?. на каждой строке. Если объект исчезнет посреди замыкания, часть кода выполнится, а часть нет. Надёжнее один раз развернуть через guard let self else { return }.