В каком случае `weak` ссылка в Swift станет `nil`?

Ответ

weak ссылка автоматически становится nil, когда объект, на который она указывает, деаллоцируется системой управления памятью (ARC). Это основной механизм для разрыва сильных циклов ссылок без риска краша.

Пример с циклом и делегатом:

protocol DataLoaderDelegate: AnyObject { // Протокол должен быть class-bound
    func dataLoadingDidFinish()
}

class DataLoader {
    weak var delegate: DataLoaderDelegate? // 1. Слабая ссылка на делегата
    func loadData() {
        // Загрузка данных...
        delegate?.dataLoadingDidFinish() // 2. Безопасный вызов через опционал
    }
}

class ViewController: UIViewController, DataLoaderDelegate {
    let loader = DataLoader()

    override func viewDidLoad() {
        super.viewDidLoad()
        loader.delegate = self // 3. Устанавливаем себя делегатом
        loader.loadData()
    }

    func dataLoadingDidFinish() {
        print("Data loaded!")
    }
}

// Сценарий:
// 1. ViewController сильно ссылается на DataLoader (свойство `loader`).
// 2. DataLoader слабо ссылается на ViewController (свойство `delegate`).
// 3. Когда пользователь закрывает экран, ViewController деаллоцируется.
// 4. Сразу после этого свойство `loader.delegate` автоматически становится `nil`.
// Цикл сильных ссылок не образовался, утечки памяти нет.
Ключевые отличия weak от unowned: weak unowned
Всегда опциональна (var). Неопциональна (let или var).
Безопасно обнуляется. При обращении к деаллоцированному объекту — краш.
Используется, когда время жизни ссылаемого объекта может быть короче. Используется, когда время жизни ссылаемого объекта точно больше или равно.

Ответ 18+ 🔞

Слушай, вот это вообще классика, блядь! Просто пиздец как важно понять, а то потом приложение твоё нахуй сольётся, а ты будешь сидеть и чесать репу: "И чё это оно память жрёт, как не в себя?".

Ну смотри, weak — это такая ссылка, которая не держит объект за яйца. Представь, ты держишь воздушный шарик за ниточку — это strong-ссылка. А weak — это как будто ты просто пальцем на него показываешь и говоришь: "Вон он, летит!". Если шарик лопнет (объект деаллоцируется), твой палец просто будет тыкать в пустоту, то есть в nil. И всё, никакого краша, просто пустота. Удобно, блядь!

Вот смотри на этот код, тут вся суть:

protocol DataLoaderDelegate: AnyObject { // Важно! Без `AnyObject` weak-ссылку не сделать, ёпта!
    func dataLoadingDidFinish()
}

class DataLoader {
    weak var delegate: DataLoaderDelegate? // 1. СЛАБАЯ, Карл! Слабая ссылка.
    func loadData() {
        // Имитируем какую-нибудь ебучую загрузку...
        delegate?.dataLoadingDidFinish() // 2. Вызов безопасный. Если делегата уже нет — просто ничего не произойдёт.
    }
}

class ViewController: UIViewController, DataLoaderDelegate {
    let loader = DataLoader() // Strong ссылка! ViewController держит loader за жопу.

    override func viewDidLoad() {
        super.viewDidLoad()
        loader.delegate = self // 3. А вот loader на нас ссылается СЛАБО.
        loader.loadData()
    }

    func dataLoadingDidFinish() {
        print("Данные пришли, ура!")
    }
}

Вот что происходит, когда пользователь, такой мудак, закрывает экран:

  1. ViewController должен умереть.
  2. Но он же держит loader! А loader держит его (delegate)! Цикл, блядь, сильных ссылок! Казалось бы, пиши пропало — утечка.
  3. А нихуя! Потому что delegateweak. Это как будто loader говорит: "Я на тебя только смотрю, но не держу".
  4. ViewController благополучно сдыхает в памяти.
  5. ARC такой: "Опа, на ViewController больше никто сильно не ссылается. В пизду его!".
  6. И сразу после этого свойство loader.delegate автоматом, блядь, становится nil. Волшебство, ёпта! Цикл разорван, утечки нет, все довольны.

А теперь главное — не перепутай с unowned, а то будет тебе ебаный краш вместо тишины и спокойствия.

weak (слабак) unowned (самоуверенный уёбок)
Всегда опционал (var delegate: Delegate?). Может быть nil. Неопционал (let captain: Captain). Всегда должен быть.
Безопасный. Объект умер — ссылка стала nil. Никаких сюрпризов. Опасно-хуёвый. Объект умер, а ты попробуй к нему обратиться — получишь краш с криком "Fatal error: Attempted to read an unowned reference...".
Используй, когда непонятно, кто кого переживёт. Например, делегаты. Используй, только если ты на 150% уверен, что объект, на который ссылаешься, будет жить дольше тебя. Например, [unowned self] в замыкании, которое точно выполнится до смерти self.

Короче, запомни: сомневаешься — ставь weak. Хочешь экстрима и потом искать, где крашнулось — ставь unowned. В рот меня чих-пых, сколько раз уже на этом обжигались!