В каких случаях в Swift используется weak-ссылка?

Ответ

Ключевое слово weak используется для объявления слабых ссылок, которые не увеличивают счетчик сильных ссылок (strong reference count) ARC. Это основной механизм предотвращения циклов сильных ссылок (strong retain cycles), приводящих к утечкам памяти.

Типичные случаи применения:

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

    class ViewController: UIViewController {
        weak var delegate: MyViewControllerDelegate? // Ссылка на делегата должна быть weak
    }

    Объект не должен "владеть" своим делегатом.

  2. Замыкания (Closures), захватывающие self:

    class DataLoader {
        func loadData(completion: @escaping (Result<Data, Error>) -> Void) {
            URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
                // Используем weak self, чтобы DataLoader мог освободиться
                self?.handleResponse(data: data, error: error)
                completion(...)
            }.resume()
        }
    }
  3. Двунаправленные связи в родительско-дочерних отношениях, где дочерний объект ссылается на родительский.

Свойства weak-ссылок:

  • Автоматическое обнуление (Zeroing): Когда объект, на который она указывала, освобождается, weak-ссылка автоматически становится nil.
  • Всегда optional: Weak-ссылки должны быть объявлены как опциональные (var) переменные.
  • Только для классов: Применяется только к ссылочным типам (экземплярам классов).

Альтернатива unowned: Используется, когда ссылка всегда будет валидной на протяжении жизни захватывающего объекта (например, при строгом порядке инициализации). В отличие от weak, unowned ссылки не являются опциональными и не обнуляются, что может привести к крашу при обращении к освобожденному объекту.

Ответ 18+ 🔞

Ну слушай, раз уж зашла речь про эти ваши слабые ссылки, weak там всякие... Это ж, блядь, как в жизни: если ты будешь за всё хвататься мёртвой хваткой, то в итоге нихуя не отпустишь и всё сгниёт у тебя в руках. Вот и память в айфоне — она тоже не резиновая, ёпта!

Смотри, в чём суть: когда ты создаёшь объект, система начинает за ним следить — сколько у него «друзей», которые на него ссылаются. Это счётчик сильных ссылок. Пока он больше нуля, объект живёт. А weak — это как знакомый, который тебя знает, но в твоей судьбе не участвует. Он не увеличивает этот счётчик. И если все нормальные «друзья» (сильные ссылки) от объекта отвернутся, система его прибьёт, а все слабые ссылки автоматом станут nil. Красота, да? Автоматическое обнуление, ёбана в рот!

Где это нужно? Да везде, где может возникнуть цикл сильных ссылок — это когда два объекта держат друг друга за жопу, и никто из них не может освободиться. Классика жанра:

  1. Делегаты, блядь. Ну серьёзно, представь: у тебя есть ViewController и какой-нибудь Manager. Вьюха назначает менеджера своим делегатом. Если эта ссылка будет сильной, то получится, что вьюха владеет менеджером, а менеджер (через какую-нибудь внутреннюю хуйню) владеет вьюхой. И вот они уже оба в памяти навечно, как в браке без возможности развода. Поэтому делегат — всегда weak!

    class MyViewController: UIViewController {
        weak var dataSource: SomeDataSource? // Вот так правильно, ёпта!
    }
  2. Замыкания, сука. Вот это вообще частый пиздец. Замыкание по умолчанию хватает всё, что внутри него используют, и держит это крепко. Если внутри замыкания ты используешь self, а само замыкание хранится внутри этого же self — привет, цикл! Объект не может умереть, потому что его же замыкание на него ссылается. Поэтому пишем [weak self]:

    networkService.downloadShit { [weak self] result in
        // Теперь self — опциональный. Если объект уже умер, то тут просто будет nil.
        self?.handleResult(result) // Вот так, с вопросиком.
    }

    Иначе твой объект будет висеть в памяти, пока приложение не сдохнет. А ты потом удивляешься: «Чё это у меня память растёт?».

  3. Родители и дети. Допустим, у тебя есть родительский объект, который создаёт дочерний и хранит его в сильной ссылке — это нормально, он им владеет. Но если этому ребёнку вдруг понадобится ссылаться на родителя, и он сделает это сильно — опять петля, опять оба бессмертны. Поэтому ребёнок ссылается на родителя через weak.

Важные нюансы, блядь:

  • weak ссылка всегда опциональная (var). Потому что она может в любой момент стать nil — это её фича, а не баг.
  • Работает только с классами. Со структурами и енумами это не нужно, они же не ссылочные.
  • Есть ещё зверь unowned — это как weak, но не опциональный. Его используют, когда уверены на 150%, что объект, на который ссылаются, переживёт того, кто на него ссылается. Но если ошибёшься — краш, ёбта! Обращение к освобождённой памяти. unowned — это как друг, который клянётся, что никогда не умрёт. А ты ему веришь.