В каких случаях счетчик сильных ссылок (retain count) на объект в ARC не увеличивается?

Ответ

Счетчик сильных ссылок управляется автоматически (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: «Знаешь что? Не трогай тут, я сам разберусь». Чтобы циклов не было или чтобы ручками память покрутить, как в старые добрые времена, когда трава была зеленее. А то так и до утечек недалеко, ядрёна вошь!