Ответ
Ссылочные типы (class) в Swift управляются через Automatic Reference Counting (ARC). Память освобождается, когда счетчик ссылок на объект становится равным нулю.
Основные инструменты управления:
strong(сильная ссылка, по умолчанию): Увеличивает счетчик ссылок. Объект не будет уничтожен, пока существует хотя бы одна сильная ссылка на него.weak(слабая ссылка): Не увеличивает счетчик ссырок. Автоматически становитсяnil, когда объект освобождается. Всегда должна быть опциональной (var).weak var delegate: MyDelegate?unowned(бесхозная ссылка): Аналогичнаweak, но не является опциональной. Предполагает, что объект будет существовать дольше, чем ссылка на него. При обращении к освобожденномуunownedобъекту произойдет краш.unowned let parentViewController: UIViewController // Используется, когда parent гарантированно живет дольше
Проблема: Цикл сильных ссылок (Retain Cycle) Возникает, когда два объекта сильно ссылаются друг на друга, не позволяя ARC освободить память.
class Person {
var apartment: Apartment? // Сильная ссылка на Apartment
deinit { print("Person освобожден") }
}
class Apartment {
var tenant: Person? // Сильная ссылка на Person
deinit { print("Apartment освобожден") }
}
var john: Person? = Person()
var unit4A: Apartment? = Apartment()
john?.apartment = unit4A // Apartment.refCount = 2
unit4A?.tenant = john // Person.refCount = 2
// ЦИКЛ СОЗДАН!
john = nil // Person.refCount = 1 (осталась ссылка из unit4A.tenant)
unit4A = nil // Apartment.refCount = 1 (осталась ссылка из john.apartment)
// Оба объекта НИКОГДА не будут освобождены, утечка памяти.
Решение: Разорвать цикл, заменив одну из сильных ссылок на weak или unowned.
class Apartment {
weak var tenant: Person? // Теперь ссылка слабая
}
// После этого при обнулении john и unit4A оба объекта успешно деинициализируются.
Типичные сценарии:
- Делегаты и протоколы: Ссылки на делегаты почти всегда должны быть
weak. - Замыкания (Closures): Замыкания захватывают (
capture) сильные ссылки на используемые объекты. Используйте список захвата[weak self]или[unowned self], чтобы избежать цикла, если замыкание хранится в самом захватываемом объекте.networkService.fetchData { [weak self] result in guard let self = self else { return } // Проверка на существование self.updateUI(with: result) }