Ответ
Счетчик сильных ссылок управляется автоматически (ARC), но есть ситуации, когда его увеличение (retain) не происходит:
-
weakиunownedссылки:weak var weakRef: MyClass? = object // Не увеличивает retain count -
Unmanaged<T>: Прямая работа с объектами Core Foundation или обход ARC.let unmanaged = Unmanaged.passUnretained(object) -
Временные объекты в autorelease pool: Объект может быть создан и возвращен без явного владения вызывающим кодом, временно находясь в пуле.
-
Оптимизации компилятора: ARC может опускать пары
retain/release, если анализ потока данных доказывает, что объект гарантированно жив в нужной области видимости. -
Захват в замыкании по weak/unowned:
closure = { [weak self, unowned delegate] in self?.doSomething() // retain count self не увеличен delegate.call() // retain count delegate не увеличен } -
Некоторые Core Foundation объекты (
CFTypeRef): Не участвуют в ARC Swift, если для них не используется toll-free bridging.
Ключевой вывод: Использование weak/unowned и Unmanaged — это основные способы явно избежать увеличения счетчика ссылок для предотвращения циклов сильных ссылок или ручного управления памятью.
Ответ 18+ 🔞
Вот тебе на, сидишь такой, думаешь — ARC, блядь, всем заведует, всё сам делает, красота! А потом раз — и у тебя цикл сильных ссылок, и приложение твоё, как та Муму, на дно пошло. Так вот, слушай сюда, не всё так просто, ёпта!
Счётчик-то сильных ссылок он в основном автоматом прыгает, это да. Но есть, сука, такие закулисные моменты, когда этот самый retain него не происходит, и объект может тебе в самый неподходящий момент вылететь в трубу, как пробка.
Первое и главное — weak и unowned. Ну это, блядь, классика жанра, чтобы циклов не было.
weak var слабаяСсылка: MyClass? = object // Счётчик НЕ подскочил, сидит на нуле, как будто её и нет!
Поставил weak — и всё, можешь не надеяться, что объект будет держаться только за счёт этой ссылки. Она, сука, не в счётчике! unowned — та же песня, только ещё опаснее, ибо предполагает, что объект точно жив, а если нет — краш тебе в ебало.
Второе — Unmanaged<T>. Это, блядь, такой прямой выход в заповедник, где ARC не ходит. Полный ручной режим, как в старые добрые времена, когда память собирали вёдрами.
let неУправляемый = Unmanaged.passUnretained(object) // Никаких retain'ов, только хардкор!
Используется, когда общаешься с каким-нибудь древним Core Foundation или хочешь сам, как бог, решать, когда объекту пиздец.
Третье — autorelease pool. Объект мог родиться внутри какого-нибудь вспомогательного метода, который его в пул отпустил, и он там плавает, пока пул не дренируют. Твоя ссылка на него могла и не увеличить счётчик, он просто временный гость.
Четвёртое — компилятор, хитрая жопа. Он иногда смотрит на код и говорит: «Да тут и так ясно, что объект живёт, зачем лишний раз retain/release вызывать?». И выкидывает их нахуй, как ненужный хлам. Оптимизация, ёпта!
Пятое — захват в замыкании по weak/unowned. Ну это, блядь, святое.
closure = { [weak self, unowned delegate] in
self?.doSomething() // self? — а потому что weak, счётчик не тронут!
delegate.call() // unowned — тоже нихуя не увеличил, живёшь на доверии, как на тонком льду.
}
Не сделаешь так — получишь цикл, и замыкание с объектом будут держать друг друга в могиле вечно.
Шестое — всякие Core Foundation объекты (CFTypeRef). Они, блядь, из другого мира, ARC Swift на них по умолчанию не распространяется. Только если ты специально сделаешь toll-free bridging, тогда да.
Ключевой вывод, блядь, который в башку надо вбить: Использование weak/unowned и Unmanaged — это твои основные легальные способы сказать ARC: «Знаешь что? Не трогай тут, я сам разберусь». Чтобы циклов не было или чтобы ручками память покрутить, как в старые добрые времена, когда трава была зеленее. А то так и до утечек недалеко, ядрёна вошь!