Когда в Swift используется `weak var`?

Ответ

weak var используется для объявления слабой изменяемой ссылки с целью предотвращения цикла сильных ссылок (retain cycle). Слабая ссылка не увеличивает счетчик ссылок (retain count) и автоматически становится nil при освобождении объекта.

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

  1. Делегаты (Delegates):

    protocol ViewControllerDelegate: AnyObject {}
    
    class ChildViewController {
        weak var delegate: ViewControllerDelegate? // Во избежание цикла: родитель -> ребенок -> родитель
    }
  2. Замыкания (Closures), захватывающие self:

    class DataManager {
        var onDataUpdated: (() -> Void)?
    
        func fetchData() {
            networkService.requestData { [weak self] data in
                // Захват `weak self` предотвращает утечку памяти,
                // если `DataManager` будет освобожден до вызова замыкания.
                self?.process(data)
            }
        }
    }

Важные правила:

  • weak применим только к ссылочным типам (классам).
  • weak ссылка всегда должна быть опциональной (var ...?), так как может стать nil.
  • Использование weak не гарантирует немедленной деаллокации, но разрывает цикл, позволяя системе освободить память.

Ответ 18+ 🔞

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

Так вот, weak var — это как взять и сказать: «Слушай, я на тебя ссылаюсь, но без фанатизма, я не буду тебя держать силой». Это слабая ссылка, она не увеличивает счётчик владения. И если объект, на который ты слабо ссылаешься, вдруг решит сдохнуть (деаллоцироваться), твоя ссылка просто станет nil. Чисто, аккуратно, без скандалов.

Где это самое слабое звено пригождается?

  1. Делегаты, блядь. Классика жанра. Родитель держит ребёнка, а ребёнок хочет держать родителя через делегат. Если держать сильно — получается та самая мертвая хватка, оба в аду.

    protocol BossManDelegate: AnyObject {} // Важно! Протокол только для классов.
    
    class WorkerAnt {
        weak var boss: BossManDelegate? // Вот так правильно. Босс может уволиться, а я не буду висеть на нём трупом.
    }
  2. Замыкания, эти прожорливые твари. Захватил self в замыкании — и привет, цикл. Особенно если замыкание живёт дольше, чем сам объект.

    class Downloader {
        var onFinish: (() -> Void)?
    
        func startDownload() {
            someNetworkCrap { [weak self] data in // Захватываем self слабо, как последнего труса!
                // А внутри уже осторожно распаковываем.
                self?.handleData(data) // Если self уже нет — просто тихо сдохнем, ничего не делая.
            }
        }
    }

Железные правила, которые нарушать — себя не уважать:

  • weak — это только для классов. На структуры или енумы не натянешь, они не в той весовой категории.
  • weak ссылка — обязательно опционал (var ...?). Потому что её судьба — в любой момент превратиться в nil, и нужно быть к этому готовым.
  • Это не волшебная таблетка от всех болезней. Это разрыв петли. Дальше система сама разберётся, когда кого прибить. Главное — дать ей такую возможность, а не держать всё в ежовых рукавицах.

Короче, если видишь, что два объекта смотрят друг на друга влюблёнными глазами и не хотят расставаться — вставляй weak. Разводи их, блядь, ради их же блага.