Что такое retain cycle и memory leak в контексте управления памятью в Swift (ARC)?

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

Ответ

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

Memory Leak (Утечка памяти) — более общее понятие, обозначающее любую ситуацию, когда выделенная память не освобождается, хотя больше не нужна программе. Retain cycle — одна из самых распространенных причин утечек в Swift.

Пример retain cycle

class Person {
    var apartment: Apartment?
    // ...
}

class Apartment {
    var tenant: Person? // СИЛЬНАЯ ссылка -> Потенциальный цикл!
    // ...
}

var john: Person? = Person()
var unit4A: Apartment? = Apartment()

john!.apartment = unit4A // Person strong -> Apartment
unit4A!.tenant = john    // Apartment strong -> Person
// ЦИКЛ СОЗДАН!

john = nil
unit4A = nil
// Оба объекта НЕ освобождены! Утечка памяти.

Решение: weak и unowned ссылки

Используйте weak или unowned, чтобы разорвать цикл сильных ссылок.

  • weak: Используется, когда ссылка может стать nil в будущем (опциональная ссылка).
    class Apartment {
        weak var tenant: Person? // Теперь weak ссылка
    }
    // Когда `john = nil`, ARC освобождает Person.
    // Свойство `tenant` в unit4A автоматически становится nil.
  • unowned: Используется, когда ссылка никогда не станет nil в течение времени жизни объекта. Небезопасно, если объект уже освобожден.
    class Customer {
        var card: CreditCard?
    }
    class CreditCard {
        unowned let customer: Customer // Карта не может существовать без Клиента
        init(customer: Customer) {
            self.customer = customer
        }
    }

Другие причины memory leak

  • Незарегистрированные observers (NotificationCenter, KVO).
  • Сильные ссылки на self внутри замыканий (closure), захваченных объектом. Решение: использовать capture lists [weak self] или [unowned self].
  • Утечки в C-библиотеках или низкоуровневом коде.