> В чем разница между weak и unowned ссылками в Swift и когда их использовать (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Wildberries, SimbirSoft, nuum, VK, Bip.ru, Doubletapp, Совкомбанк, Masterdata, Тинькофф, Eltex, Яндекс

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Weak и unowned - оба варианта позволяют избежать retain cycle в замыканиях и свойствах. Weak всегда optional и автоматически становится nil при освобождении объекта, требует weak var. Unowned - non-optional, предполагает, что объект живёт столько же, сколько ссылка, при обращении к освобождённому объекту - crash. Weak используем для делегатов и ссылок, которые могут стать nil. Unowned - когда время жизни ссылки гарантированно не превышает время жизни объекта.

Подробное объяснение

Разница в семантике владения и безопасности. Weak ссылка - это слабая ссылка, которая не увеличивает счетчик ссылок. ARC автоматически обнуляет её (nil) при деинициализации объекта. Поэтому weak объявляется только как var и всегда optional. Unowned также не увеличивает счетчик, но не обнуляется - она остаётся "висячей" (dangling) после освобождения объекта. Обращение к unowned после деинициализации вызывает runtime crash.

Ключевое различие - гарантия времени жизни. Weak говорит: "объект может умереть в любой момент, я готов к nil". Unowned говорит: "объект гарантированно жив, пока я существую". На практике это означает: если есть хоть малейший шанс, что объект освободится раньше, чем ссылка - используй weak. Если уверен в порядке жизни - unowned чуть более эффективен (без unwrapping) и выражает намерение.

В замыканиях выбор зависит от capture list. Если замыкание может пережить захваченный объект - weak. Если замыкание и объект живут в одном жизненном цикле (например, замыкание - свойство того же класса) - unowned допустим, но weak безопаснее.

На практике

Для делегатов и data source - всегда weak. Для замыканий, хранящихся в свойствах класса, - чаще weak, потому что сложно гарантировать порядок деинициализации. Unowned оправдан в редких случаях: например, когда замыкание вызывается синхронно внутри метода того же объекта, или когда объект гарантированно живёт дольше замыкания (например, замыкание - свойство дочернего объекта, который удаляется раньше родителя).

В SwiftUI и Combine unowned встречается в sink, но там лучше weak из-за асинхронности. В GCD-замыканиях - только weak, потому что замыкание может выполниться после освобождения объекта. В циклах с двумя объектами, где один владеет другим, а второй ссылается на первого - weak на обратную ссылку.

Пример кода

SWIFT
class Child {
var onEvent: (() -> Void)?
deinit { print("Child deinit") }
}
class Parent {
var child: Child?
weak var weakRef: Child?
unowned var unownedRef: Child!
func setup() {
let c = Child()
child = c
weakRef = c
unownedRef = c // опасно: unowned на объект, которым владеет другой
}
func testClosure() {
child?.onEvent = { [weak self] in
guard let self else { return }
print(self)
}
// или [unowned self] - только если self точно жив
}
}

Как отвечать на собеседовании

Начни с главного: оба не увеличивают retain count, разница в безопасности и optional. Приведи конкретный сценарий: делегат - weak, потому что владелец может освободиться. Замыкание в свойстве - weak, потому что жизненный цикл непредсказуем. Unowned - только когда есть железная гарантия. Покажи понимание retain cycle и ARC. Упомяни, что weak требует var и optional, а unowned - нет. Если спросят про производительность - weak чуть дороже из-за обнуления, но это микрооптимизация.

Что проверяет интервьюер

  • Понимание ARC и механизма подсчёта ссылок
  • Умение анализировать жизненный цикл объектов
  • Знание синтаксических ограничений (weak - var optional, unowned - non-optional)
  • Практический опыт: когда реально возникает retain cycle
  • Понимание разницы между "может стать nil" и "не может стать nil" в контексте безопасности
  • Способность объяснить trade-off между безопасностью и производительностью

Типичные ошибки

  • Использование unowned там, где объект может освободиться - crash
  • Объявление weak как let - компилятор не позволит
  • Забывание guard let self в weak-замыканиях
  • Утверждение, что unowned "быстрее" - это не причина выбора
  • Использование weak для value types - бессмысленно
  • Путаница между unowned и weak в контексте замыканий: если замыкание хранится в свойстве объекта и захватывает self - это retain cycle, weak обязателен
  • Игнорирование случая, когда объект деинициализируется, но unowned ссылка остаётся - это UB, а не просто nil

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью