Что такое retain cycle в управлении памятью?

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

Ответ

Retain cycle (цикл сильных ссылок) — это ситуация, когда два или более объекта удерживают друг друга через strong-ссылки, что предотвращает их освобождение системой ARC и приводит к утечке памяти.

Почему это проблема: ARC освобождает объект только когда счётчик сильных ссылок на него становится равным нулю. В цикле счётчик никогда не достигнет нуля.

Пример возникновения:

class Person {
    var dog: Dog?
    deinit { print("Person освобождён") }
}

class Dog {
    var owner: Person? // Strong reference → создаёт цикл
    deinit { print("Dog освобождён") }
}

var john: Person? = Person()
var fido: Dog? = Dog()
john?.dog = fido
fido?.owner = john // Цикл создан!
john = nil
fido = nil // Ни один deinit не вызовется, память не освободится

Способы решения:

  1. weak ссылка: Используется, когда ссылаемый объект может стать nil.
    class Dog {
        weak var owner: Person?
    }
  2. unowned ссылка: Используется, когда ссылаемый объект гарантированно существует в течение всей жизни ссылающегося объекта (риск краша, если объект освобождён).
    class CreditCard {
        unowned let customer: Customer // Карта не может существовать без клиента
    }

Частый случай в замыканиях: Замыкания — reference types. Захват self без указания weak создаёт retain cycle.

lazy var someClosure: () -> Void = { [weak self] in // Важно: [weak self]
    self?.doSomething()
}