Ответ
Управление памятью в iOS на Swift основано на механизме ARC (Automatic Reference Counting). ARC автоматически отслеживает количество сильных ссылок на каждый экземпляр класса и освобождает память, когда счетчик ссылок становится равным нулю.
Типы ссылок:
- Strong (Сильная, по умолчанию): Увеличивает счетчик ссылок (
retain count) на 1. Объект живет, пока на него есть хотя бы одна сильная ссылка. - Weak (Слабая): Не увеличивает счетчик ссылок. Ссылка автоматически становится
nil, когда объект освобождается. Всегда объявляется какvarи optional.weak var delegate: MyDelegate? - Unowned (Бесхозная): Аналогична weak, но не является optional. Предполагается, что объект, на который ссылаются, никогда не будет освобожден раньше ссылающегося объекта. При нарушении этого ведет к крашу.
unowned let parentController: UIViewController // parentController должен пережить этот объект
Главная проблема: Циклические сильные ссылки (Retain Cycle) Возникает, когда два объекта удерживают друг друга сильными ссылками, не давая ARC освободить их.
Пример и решение:
class Person {
var apartment: Apartment? // Сильная ссылка
deinit { print("Person освобожден") }
}
class Apartment {
weak var tenant: Person? // СЛАБАЯ ссылка разрывает цикл
deinit { print("Apartment освобожден") }
}
var john: Person? = Person()
var unit4A: Apartment? = Apartment()
john?.apartment = unit4A // Apartment.refCount = 1 (от john)
unit4A?.tenant = john // Person.refCount = 1 (от unit4A, но weak!)
john = nil // Person.refCount -> 0, Person освобождается
unit4A = nil // Apartment.refCount -> 0, Apartment освобождается
// Оба deinit будут вызваны.
Типичные сценарии и решения:
- Делегаты: Всегда объявляйте как
weak var. - Замыкания (Closures): Используйте список захвата
[weak self]или[unowned self], если замыкание может создать цикл сself.networkService.completion = { [weak self] result in self?.handle(result) // self — optional } - Инструменты для отладки: Используйте Debug Memory Graph в Xcode и инструмент Leaks в Instruments для поиска циклов.