Когда изменяется счетчик ссылок (retain count) объекта?

Ответ

Счетчик ссылок изменяется при выполнении операций владения. В ручном управлении памятью (MRC) — при явных вызовах. В ARC — при событиях, которые компилятор транслирует в эти вызовы.

Операции, увеличивающие счетчик (retain):

  • Присваивание объекта сильной (strong) ссылке.
  • Помещение объекта в коллекцию (массив, словарь), которая хранит сильные ссылки.
  • Явный вызов retain (в MRC).

Операции, уменьшающие счетчик (release):

  • Сильной ссылке присваивается nil или другой объект.
  • Сильная ссылка выходит из области видимости (конец функции, блока).
  • Объект удаляется из коллекции.
  • Владеющий объект (например, родительский UIView) сам деаллоцируется.
  • Явный вызов release (в MRC).

Псевдокод жизненного цикла в ARC:

// Компилятор генерирует примерно такой код:
class MyObject {}

func createObject() {
    let obj = MyObject()          // retain(obj) -> count = 1
    use(obj)
    // Конец области видимости `obj` -> release(obj) -> count = 0 -> deinit
}

Почему это важно: Понимание этих моментов помогает избегать retain cycles (когда два объекта держат сильные ссылки друг на друга, и счетчик никогда не падает до нуля).

Ответ 18+ 🔞

Да ты посмотри, какая хуйня интересная творится с этими ссылками! Ну, то есть, не ссылками, а счётчиком владения. Представь, у каждого объекта в памяти — своя личная табличка «Сколько рук на меня смотрят». И вот, блядь, компилятор, этот хитрожопый сукин сын, постоянно её обновляет.

Когда счётчик идёт в гору (типа retain):

  • Ты, мудак, взял и ткнул пальцем в объект сильной ссылкой — раз! Плюсик.
  • Запихнул его в массив или словарь, которые тоже смотрят на всё жадными глазами — два! Ещё плюсик.
  • Ну или в старые добрые времена (MRC) ты сам орал retain, как торговка на рынке.

А когда счётчик летит в пизду (типа release):

  • Ты взял свою сильную ссылку и сказал ей: «Иди нахуй, будь nil». Минусик.
  • Ссылка просто вышла за скобки функции — кончилась её жизнь, блядь. Автоматический минусик.
  • Выковырял объект из массива — опаньки, коллекция на него больше не пялится. Минусик.
  • Родительский объект, который его держал, сам сдох. Ну и ясно, минусик.
  • Опять же, в MRC ты сам матерился и вызывал release.

И вот как это всё, сука, выглядит изнутри, когда ARC работает:

// Компилятор втихаря делает примерно такую подлянку:
class MyObject {}

func createObject() {
    let obj = MyObject()          // retain(obj) -> счётчик = 1 (родился, сука!)
    use(obj)
    // А тут кончилась область видимости `obj` -> release(obj) -> счётчик = 0 -> и сразу deinit, в утиль!
}

А зачем этот цирк, спросишь? Да затем, ёпта, чтобы не попасть в ебучую retain cycle! Это когда два упыря начинают держаться друг за друга сильными ссылками, как два пьяных мужика у подъезда: «Я тебя не пущу!» — «А я тебя не отпущу!». И оба нихуя не освобождаются, память течёт, а приложение потом накрывается медным тазом. Понимание, где какой счётчик щёлкает, — это и есть твой спасательный круг от таких пидарасов в коде.