Как ARC автоматически расставляет retain и release?

Ответ

ARC (Automatic Reference Counting) автоматически вставляет вызовы retain и release, отслеживая время жизни сильных (strong) ссылок на объекты.

Правила вставки:

  • retain: Вставляется при присваивании объекта сильной ссылке (переменной, свойству, константе). Увеличивает счетчик ссылок.
  • release: Вставляется в двух случаях:
    1. Когда сильная ссылка перезаписывается другим значением (или nil).
    2. Когда сильная ссылка выходит из области видимости (например, завершается функция).

Пример трассировки:

class MyClass {}

func example() {
    // Область видимости начинается
    var obj1: MyClass? = MyClass() // ARC: retain для нового объекта
    var obj2: MyClass? = obj1      // ARC: retain (присваивание obj1 в obj2)

    obj1 = nil                     // ARC: release для obj1
    // Объект еще жив, так как на него ссылается obj2

    obj2 = MyClass()               // ARC: release для старого объекта, retain для нового
    // Старый объект уничтожается здесь (счетчик = 0)
} // ARC: release для obj2 (для нового объекта) при выходе из области видимости

Особые случаи:

  • weak ссылки: Не вызывают retain, поэтому не влияют на счетчик. ARC автоматически обнуляет (nil) weak-переменную после release последней сильной ссылки.
  • unowned ссылки: Также не вызывают retain, но не обнуляются. Ответственность за обеспечение времени жизни объекта лежит на программисте.
  • Циклы сильных ссылок: ARC не может их разорвать, что приводит к утечкам памяти. Решение — использование weak или unowned.

Ответ 18+ 🔞

Да ты посмотри на эту магию, блядь! ARC — это ж как домовой в твоём коде, сука, только вместо того, чтобы носки воровать, он за тобой retain и release раскидывает, чтобы память не текла, как из дырявого ведра.

Вот смотри, как этот хитрожопый компилятор работает. Берёт он твой красивый Swift-код и начинает туда, внатуре, свои пасхалки вставлять.

Где он что суёт:

  • retain: Впендюривается ровно в тот момент, когда ты какую-нибудь переменную, этого, сильную, блядь, нацеливаешь на свежеиспечённый объект. Счётчик ссылок — раз! — и подрос, ёпта.
  • release: А вот эта пакость лезет в двух случаях, когда тебя подловить можно:
    1. Когда ты свою же сильную ссылку, неблагодарная тварь, переписываешь на другую (или на nil).
    2. Когда переменная, сука, выходит из области видимости — типа функция кончилась, и всё, пиши пропало.

Разбор полётов, на живом примере:

class MyClass {}

func example() {
    // Заходим в зону, блядь, ответственности
    var obj1: MyClass? = MyClass() // ARC: тихо ебёт retain на новый объект
    var obj2: MyClass? = obj1      // ARC: и ещё один retain, потому что obj2 тоже на него смотрит

    obj1 = nil                     // ARC: а тут release для obj1! Но объект жив, ёпта, его же obj2 держит!

    obj2 = MyClass()               // ARC: ой-вей! Сначала release для старого объекта (счётчик в ноль — ему пиздец), потом retain для нового.
    // Старичка тут и прикончили, блядь, память освободили.
} // ARC: а на выходе из функции — бац! — ещё release для obj2, уже по новому объекту.

А теперь, блядь, тонкости, где можно обосраться:

  • weak ссылки: Это как смотреть на чужую пиццу, блядь. Ты на неё смотришь (weak), но взять не можешь (retain не вызывается). И как только последний жадный едок (сильная ссылка) доедает свой кусок, тебе так вежливо говорят: «Всё, пиццы нет, иди нахуй» — и твоя weak-переменная становится nil.
  • unowned ссылки: А это, сука, как по тонкому льду идти. Тоже retain не дергает, но и nil тебе никто не подставит. Думаешь, объект жив? А он уже на дне. И ты, довольный, шагаешь — и проваливаешься в краш, блядь. Ответственность — твоя, собака сука.
  • Циклы сильных ссылок: Вот это, блядь, классика! Два объекта держат друг друга за жопу сильными ссылками. ARC смотрит на это, чешет репу и говорит: «Ну, ёпта, оба живы, нихуя не сделаешь». А они на самом деле уже мертвы для всего мира, но память жрут. Спасение — только weak или unowned, чтобы разорвать этот ебаный круг.