Как weak-ссылка влияет на подсчёт ссылок и жизненный цикл объекта в ARC?

Ответ

weak-ссылка не увеличивает счётчик сильных ссылок (retain count) объекта в ARC (Automatic Reference Counting). Это ключевое свойство, которое предотвращает циклы сильных ссылок (retain cycles).

Принцип работы:

  1. Нет удержания: weak-ссылка не мешает ARC освободить объект, когда на него не остаётся сильных ссылок.
  2. Автоматическое обнуление: После освобождения объекта все weak-ссылки на него автоматически устанавливаются в nil. Это предотвращает краши при обращении к «висячим» указателям.

Пример:

class Server {
    let name: String
    init(name: String) { self.name = name; print("(name) создан") }
    deinit { print("(name) удалён") }
}

var primaryServer: Server? = Server(name: "Primary") // Retain count = 1
weak var backupServerRef = primaryServer // Retain count остаётся = 1

primaryServer = nil // Сильных ссылок нет. Вывод: "Primary удалён"
print(backupServerRef) // nil. Безопасно, не вызывает краш.

Типичные сценарии использования:

  • Делегаты (delegates): Почти всегда объявляются как weak, чтобы объект не удерживал своего делегата.
  • Замыкания (closures): Использование [weak self] для предотвращения цикла, когда self удерживает замыкание, а замыкание удерживает self.
  • Родительско-дочерние связи: Дочерний объект может иметь weak-ссылку на родительский.

Важно: weak-ссылки в Swift всегда должны быть опциональными переменными (var) типа, так как они могут стать nil.

Ответ 18+ 🔞

О, смотри-ка, какой умный припёрся с ARC и weak-ссылками! Давай разжуём эту тему так, чтобы даже у кота, который по клавиатуре ходил, мозги не вскипели.

Слушай сюда, мой друг. Представь, что у тебя есть объект — ну, скажем, здоровенный такой сервер, который орет «Я живой!». Пока на него кто-то сильно смотрит (strong reference), он живёт и пыхтит. Но вот появляется эта хитрая жопа — weak ссылка.

В чём её подвох, спросишь ты? А в том, что она — предатель!

Она не увеличивает счётчик сильных ссылок (retain count). Вообще. Ни на йоту. Это как если бы ты крикнул объекту: «Эй, дружище, я тут за тобой наблюдаю, но если тебя начнут убивать — я даже пальцем не пошевелю, спасать не буду». И объект ей верит, сука.

А дальше магия, блядь:

  1. Не держит. Когда последний сильный ссылочник машет ручкой и говорит «прощай», ARC с чистой совестью отправляет объект в утиль. А weak-ссылка? А ей похуй. Она просто стоит и смотрит, как его деаллоцируют.
  2. Автоматически обнуляется. И вот тут — гениальность, ёпта! После того как объект стал пылью, weak-ссылка не превращается в ту самую «висячую», от которой крашится всё к хуям. Нет! Она вежливо становится nil. Как будто так и было. Безопасно, культурно. Обратишься к ней — получишь nil, а не вылет в космос.

Пример, чтобы совсем охуенно стало понятно:

class Server {
    let name: String
    init(name: String) { self.name = name; print("(name) создан, блядь!") }
    deinit { print("(name) удалён в пизду!") } // Прощай, друг
}

var primaryServer: Server? = Server(name: "Primary") // Сильная ссылка. Retain count = 1. Живёт.
weak var backupServerRef = primaryServer // Weak ссылка. Retain count ВСЁ ЕЩЁ = 1. Не держит!

primaryServer = nil // Последняя сильная ссылка ушла. Вывод: "Primary удалён в пизду!"
print(backupServerRef) // nil. Тишина. Спокойствие. Никакого краша.

Где эту штуку впендюривают, чтобы не облажаться?

  • Делегаты (delegates): Это святое, ёпта! Объект почти никогда не должен сильно держать своего делегата. Иначе получится «я тебя породил — я тебя и похороню», а делегат уже давно похоронен быть должен. weak var delegate: SomeDelegateProtocol? — золотое правило.
  • Замыкания (closures): Вот тут часто все и ебутся. Классика: объект держит замыкание, а замыкание внутри держит self. Цикл, пиздец, память не освобождается. Спасение — [weak self] in. Говоришь замыканию: «Слушай, на self можешь ссылаться, но только слабенько, а то сами с усами».
  • Связи в иерархии: Дочерний объект может слабо ссылаться на родителя, чтобы не мешать тому умереть, когда придёт время.

Важный нюанс, про который все забывают, пока не наступят на грабли:
weak-ссылки в Swift — обязательно опциональные переменные (var). Потому что их состояние — nil или объект — может меняться. Константой (let) её не сделаешь, это же пиздец как логично.