Ответ
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-библиотеках или низкоуровневом коде.
Ответ 18+ 🔞
А, ну это классика, блядь! Сейчас объясню, как эти два понятия друг друга ебут, но по-разному.
Смотри, Retain Cycle — это как два мудака в баре упёрлись лбами и оба не хотят отойти первый. Каждый держит другого за грудки (strong reference), и оба стоят, блядь, до посинения. Система (наш ARC) смотрит на них и думает: «Ну, раз они друг друга держат, значит, ещё нужны, пусть стоят». А они уже давно друг другу не нужны, просто упёрлись, пидарасы. Это частный, но овердохуища распространённый случай утечки.
А Memory Leak — понятие пошире. Это любая ситуация, когда память выделили, а потом забыли, где положили ключи от неё, и она так и болтается, никому не нужная. Retain cycle — это просто самый популярный способ такую утечку устроить, типа как через главный вход вломиться.
Вот, смотри на этот пиздец в коде. Классическая ловушка для молодых и глупых.
class Person {
var apartment: Apartment? // Чувак знает про квартиру
// ...
}
class Apartment {
var tenant: Person? // Квартира знает про жильца. И ссылка СИЛЬНАЯ! Вот тут пизда.
// ...
}
var john: Person? = Person() // Родился Джон
var unit4A: Apartment? = Apartment() // Построили квартиру 4А
john!.apartment = unit4A // Джон взял ключи от квартиры (strong ref)
unit4A!.tenant = john // Квартира прописала Джона (тоже strong ref)
// ВСЁ, ПИЗДЕЦ, ЦИКЛ! Они теперь в браке, из которого нет развода.
john = nil // Мы пытаемся Джона убить
unit4A = nil // И квартиру снести
// А нихуя! Они ж друг друга держат! В памяти навечно повисли два призрака, которые ебут друг другу мозги.
Как с этим бороться? Надо одного из них ослабить, сделать слабаком. Для этого есть weak и unowned.
weak— это как сказать: «Слушай, я тебя знаю, но если тебя вдруг не станет, я не буду в истерике падать, я просто забуду тебя (стануnil)». Используй, когда объект может испариться.class Apartment { weak var tenant: Person? // Теперь ссылка слабая, не удерживающая! } // Теперь когда мы делаем `john = nil`, ARC смотрит: «Ага, на Джона strong-ссылок больше нет, только weak от квартиры». БАМ! Джона удаляем. Квартира просыпается, смотрит — а tenant-то уже nil. Ну и хуй с ним.unowned— это более жёсткая хуйня. Это как сказать: «Этот объект (например, твой паспорт) не может существовать без тебя. Если тебя не станет — и его быть не должно, а если я попробую к нему обратиться — будет краш, пиздец и вылет приложения». Опасная, но иногда нужная штука.class Customer { var card: CreditCard? } class CreditCard { unowned let customer: Customer // Карта не может жить без клиента, это факт init(customer: Customer) { self.customer = customer } } // Удали клиента — карта должна стать битой. Обратишься к card.customer после удаления клиента — получишь runtime error и полные штаны.
А ещё утечки, блядь, бывают не только из-за циклов! Вот другие способы проебать память:
- Подписался на уведомления в
NotificationCenterи забыл отписаться. Теперь твой объект висит в списках, как идиот, и ждёт событий, которые уже никому не нужны. - Захватил
[self]в замыкании (closure), которое само принадлежит этому self. Опять получил пиздатый цикл. Спасение —[weak self]или[unowned self]в capture list. - Ну и классика жанра — наваял какой-нибудь низкоуровневый C-код, наколбасил
malloc, аfreeзабыл. Вот тут уже ARC не поможет, сам дурак.
Короче, следи за связями, блядь. Слабей ссылки делай, где можно. И память будет в порядке. А то нахватаешься этих циклов — и приложение твоё через полчаса будет есть оперативку, как мартышка бананы.