← Назад к вопросам

Какую проблему решают weak и unowned ссылки?

2.0 Middle🔥 301 комментариев
#Управление памятью#Язык Swift

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Проблема сильных ссылочных циклов в Swift

Weak и unowned ссылки решают фундаментальную проблему управления памятью в Swift — циклические или сильные ссылочные циклы (strong reference cycles), которые приводят к утечкам памяти.

Суть проблемы

В Swift используется Automatic Reference Counting (ARC) — система автоматического подсчета ссылок. Каждый раз, когда создается сильная ссылка на объект, его счетчик ссылок увеличивается на 1. Когда ссылка уничтожается — счетчик уменьшается. Когда счетчик достигает нуля — память освобождается.

Проблема возникает, когда два объекта имеют сильные ссылки друг на друга:

class Person {
    let name: String
    var apartment: Apartment?  // Сильная ссылка на Apartment
    
    init(name: String) {
        self.name = name
    }
}

class Apartment {
    let unit: String
    var tenant: Person?  // Сильная ссылка на Person
    
    init(unit: String) {
        self.unit = unit
    }
}

var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")

john?.apartment = unit4A  // Person ссылается на Apartment
unit4A?.tenant = john     // Apartment ссылается на Person

john = nil    // Счетчик Person не становится 0 (есть ссылка из Apartment)
unit4A = nil  // Счетчик Apartment не становится 0 (есть ссылка из Person)

После выполнения этого кода оба объекта остаются в памяти, хотя внешние переменные john и unit4A уже nil. ARC не может их освободить, потому что каждый объект удерживает другого — это и есть циклическая ссылка.

Решение через weak и unowned

Weak (слабая) ссылка

Weak ссылка не увеличивает счетчик ссылок ARC. Когда объект, на который она ссылается, освобождается, weak ссылка автоматически становится nil.

class Apartment {
    let unit: String
    weak var tenant: Person?  // СЛАБАЯ ссылка
    
    init(unit: String) {
        self.unit = unit
    }
}

Характеристики weak ссылок:

  • Всегда объявляются как var (потому что могут стать nil)
  • Всегда optional (?)
  • Автоматически становятся nil при освобождении объекта
  • Используются, когда ссылка может существовать дольше, чем объект, на который она указывает

Unowned (бесхозная) ссылка

Unowned ссылка также не увеличивает счетчик ссылок, но в отличие от weak, она никогда не становится nil. Предполагается, что объект, на который она ссылается, будет существовать всегда, пока существует сама unowned ссылка.

class Customer {
    let name: String
    var card: CreditCard?
    
    init(name: String) {
        self.name = name
    }
}

class CreditCard {
    let number: String
    unowned let customer: Customer  // БЕСХОЗНАЯ ссылка
    
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
}

Характеристики unowned ссылок:

  • Могут быть let или var
  • Не optional (вообще не могут быть nil)
  • При попытке обращения к освобожденному объекту — краш приложения
  • Используются, когда объект имеет тот же или более короткий жизненный цикл

Критерии выбора

Когда использовать weak:

  • Delegate-паттерн (делегаты почти всегда weak)
  • Обратные ссылки в parent-child отношениях, где child может существовать без parent
  • Closure capture lists, когда замыкание захватывает self, но не должно удерживать его
  • Любые ситуации, где ссылка может пережить объект
// Пример в замыкании
class NetworkManager {
    var completionHandler: (() -> Void)?
    
    func fetchData(completion: @escaping () -> Void) {
        self.completionHandler = { [weak self] in
            guard let self = self else { return }
            // Работаем с self
            completion()
        }
    }
}

Когда использовать unowned:

  • Когда два объекта имеют одинаковый жизненный цикл
  • В отношениях, где один объект никогда не существует без другого
  • Когда уверены, что ссылка всегда будет валидной
  • Для небольшого повышения производительности (не нужно проверять на nil как weak)

Практические рекомендации

  1. По умолчанию используйте weak, если не уверены
  2. Unowned применяйте только при полной уверенности, что объект будет существовать
  3. Всегда используйте capture lists в замыканиях, которые захватывают self
  4. Инструменты для обнаружения циклов: Instruments Leaks, Xcode Debug Memory Graph
  5. Паттерны проектирования: часто циклические ссылки указывают на проблемы в архитектуре

Заключение

Weak и unowned ссылки — критически важный механизм для предотвращения утечек памяти в Swift. Они позволяют создавать сложные связи между объектами, сохраняя при этом корректную работу ARC. Правильное их использование требует понимания жизненных циклов объектов и является признаком опытного iOS-разработчика. Современные приложения без правильного использования weak/unowned неизбежно сталкиваются с проблемами производительности и утечками памяти.