Зачем в Swift использовать capture list в замыканиях?

Ответ

Capture list (список захвата) явно определяет, как замыкание захватывает переменные из окружающего контекста. Без него захват происходит по сильной ссылке (strong) по умолчанию, что может привести к retain cycle (циклу сильных ссылок) и утечке памяти.

Проблема (retain cycle):

class ViewController {
    var onAction: (() -> Void)?

    func setup() {
        // Захват self по сильной ссылке -> ЦИКЛ!
        onAction = {
            self.doSomething() // self удерживает onAction, onAction удерживает self
        }
    }
}

Решение с capture list:

func setup() {
    // [weak self] делает ссылку слабой (weak)
    onAction = { [weak self] in
        self?.doSomething() // self стал Optional
    }
    // [unowned self] делает ссылку бесхозной (unowned) – используйте осторожно!
    onAction = { [unowned self] in
        self.doSomething() // Краш, если self будет освобожден
    }
}

Ключевые правила:

  1. Используйте [weak] для захвата опциональных ссылок, которые могут стать nil.
  2. Используйте [unowned] только если захваченный объект гарантированно переживет замыкание.
  3. Также можно захватывать и копировать значения: [x, y] или [someValue = self.value].

Ответ 18+ 🔞

А, ну вот, опять про эти ваши списки захвата, блядь! Смотри, сейчас объясню, как есть, без соплей.

Значит, смотри. Замыкание — оно как хитрая жопа, которая всё вокруг цепляет и держит. По умолчанию оно хватает всё сильной хваткой, как удав, блядь. И если ты внутри замыкания на self сослался, то всё, пиши пропало — получил цикл сильных ссылок, ёпта.

Вот смотри, классика жанра, пиздец как весело:

class ViewController {
    var onAction: (() -> Void)?

    func setup() {
        // Сейчас будет мясо, блядь!
        onAction = {
            self.doSomething() // Ёбушки-воробушки! Смотри что делает!
        }
    }
}

Что тут происходит, а? self держит свойство onAction. А onAction — это замыкание, которое внутри держит self. И они обнимаются, блядь, до скончания времён, как два пидараса шерстяных. Никто никого не отпускает. Память течёт, приложение в пизду. Удивление пиздец!

И вот тут на сцену выходит наш спаситель — capture list, блядь! Это такой чек-лист, где ты указываешь, КАК именно хватать переменные. Не "всех скопом в охапку", а поимённо, с указанием статуса.

func setup() {
    // Правильный подход, как у взрослых, блядь!
    // [weak self] — делаем ссылку слабой, нежной такой.
    onAction = { [weak self] in
        self?.doSomething() // И вот он, красавец, стал Optional! Теперь если self умрёт — тут просто будет nil, а не цирк с конями.
    }

    // А есть ещё вариант для смелых, блядь! [unowned self]
    onAction = { [unowned self] in
        self.doSomething() // Это как сказать: "Я уверен, что self переживёт это замыкание нахуй!" Но если ошибёшься — краш, пизда тебе в сраку, а не приложение.
    }
}

Так, Колян, запоминай правила, чтобы не обосраться:

  1. [weak] — твой главный друг. Используй, когда не уверен, кто кого переживёт. Объект может стать nil, и это норма, блядь. Замыкание просто не выполнится, и все живы.
  2. [unowned] — это как ходить по охуенно тонкому льду. Бери только если на 146% уверен, что объект будет жить дольше замыкания. Ошибся — получишь краш, и будешь потом как Герасим: "Мууу... блядь..."
  3. Можно не только ссылки ловить, но и значения копировать, блядь! [x, y] или вот так: [someValue = self.value] — скопирует значение на момент создания замыкания, и потом похуй, что с self.value будет.

Короче, суть в чём: без capture list — это дичь и мракобесие, ведущее к утечкам. С ним — контролируемый захват, порядок и чистота, блядь. Не будь мудаком, используй списки захвата. В рот меня чих-пых, вот и весь сказ!