Ответ
Счетчик ссылок изменяется при выполнении операций владения. В ручном управлении памятью (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! Это когда два упыря начинают держаться друг за друга сильными ссылками, как два пьяных мужика у подъезда: «Я тебя не пущу!» — «А я тебя не отпущу!». И оба нихуя не освобождаются, память течёт, а приложение потом накрывается медным тазом. Понимание, где какой счётчик щёлкает, — это и есть твой спасательный круг от таких пидарасов в коде.