Какие типы ссылок на объекты существуют в Swift и чем они отличаются?

«Какие типы ссылок на объекты существуют в Swift и чем они отличаются?» — вопрос из категории Управление памятью, который задают на 28% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Swift (в контексте управления памятью для классов) предоставляет три основных типа ссылок, которые отличаются своим влиянием на счетчик ссылок (Retain Count) и поведением при освобождении объекта.

Тип ссылки Влияние на Retain Count Опциональность Поведение при освобождении объекта Типичный use-case
Strong Увеличивает (+1) Optional/Non-optional Удерживает объект в памяти. Стандартная ссылка, владение объектом.
Weak Не увеличивает Всегда Optional Автоматически становится nil. Для разрыва потенциальных retain cycles (напр., delegate, дочерние ссылки на родителя).
Unowned Не увеличивает Non-optional Остается dangling (обращение → краш). Когда время жизни ссылаемого объекта гарантированно равно или длиннее времени жизни ссылки.

Примеры и различия:

class Customer {
    let name: String
    var card: CreditCard? // Сильная ссылка на CreditCard
    init(name: String) { self.name = name }
    deinit { print("(name) is being deinitialized") }
}

class CreditCard {
    let number: String
    weak var customer: Customer? // СЛАБАЯ ссылка разрывает цикл
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer // Не увеличивает retain count Customer
    }
    deinit { print("Card #(number) is being deinitialized") }
}

// Использование
var john: Customer? = Customer(name: "John")
john!.card = CreditCard(number: "1234", customer: john!)

john = nil // Customer освобождается, затем CreditCard, т.к. цикл разорван weak-ссылкой.
// Вывод в консоль:
// John is being deinitialized
// Card #1234 is being deinitialized

Unowned (пример с гарантированным временем жизни):

class Department {
    let name: String
    unowned var manager: Employee // Руководитель не может существовать без отдела
    init(name: String, manager: Employee) { ... }
}

Вывод: Используйте weak для безопасного разрыва циклов, unowned — только при абсолютной уверенности в жизненном цикле, чтобы избежать крашей.