В чём разница между использованием capture list и просто weak self в замыкании?

Ответ

Capture list — это синтаксическая конструкция для явного управления захватом переменных в замыкании. [weak self] — это частный и самый распространённый случай capture list.

Ключевое отличие: Capture list позволяет контролировать захват любых переменных из окружающего контекста, а не только self. Она делает зависимости замыкания явными и предотвращает непреднамеренные сильные захваты.

Пример 1: Только weak self (это уже capture list)

closure = { [weak self] in
    self?.doWork()
}

Пример 2: Capture list с несколькими переменными

class Processor {
    var data: Data
    var onComplete: (() -> Void)?

    func process(with formatter: Formatter) {
        // Захватываем self слабо, а formatter сильно (по умолчанию).
        // Это явно показывает, что замыкание зависит от formatter.
        someAsyncTask { [weak self, formatter] result in
            guard let self = self else { return }
            let output = formatter.format(result, using: self.data)
            self.finish(with: output)
        }
    }
}

Пример 3: Захват с переименованием (например, сильная ссылка на self)

networkService.fetchData { [weak self] result in
    // Создаём сильную ссылку на self ТОЛЬКО на время выполнения замыкания
    guard let strongSelf = self else { return }
    strongSelf.handle(result)
    strongSelf.cleanup() // safe, strongSelf гарантированно жив
}

Практические правила:

  1. Всегда используйте capture list ([weak self], [unowned self]) для явного указания захвата self во избежание retain cycles.
  2. Захватывайте другие переменные через capture list, если важно явно показать зависимость замыкания или управлять их временем жизни (например, [weak delegate]).
  3. weak self внутри тела замыкания без capture list — это ошибка. Capture list определяет, как захватывать, а использование self? внутри — это уже работа с захваченной ссылкой.

Итог: Capture list — это обязательный инструмент для безопасной работы с замыканиями, а weak self — его наиболее важный частный случай.

Ответ 18+ 🔞

А, capture list, ёпта! Ну это ж та самая штука, которая спасает от утечек памяти, когда замыкания начинают друг друга держать за жопу, как два пьяных мужика у подъезда — «я тебя не пущу, ты меня не пустишь», и оба в итоге в луже засыпают.

Смотри, суть-то проще, чем кажется. Вот представь: ты в баре, и твой друг self заказывает выпить. Если ты схватишь его в охапку и будешь держать как одержимый (сильный захват), он никуда не денется, даже если ему уже пора к семье. Но если он тебя попросит: «давай, брат, держи меня за рукав, на всякий» (weak capture), то он сможет уйти, когда захочет, а ты просто рукав в руках останется.

Пример 1: Классика — weak self

closure = { [weak self] in
    self?.doWork() // Тут self — уже опциональный, как пропуск на закрытый завод.
}

Это и есть capture list, блядь! Ты явно говоришь: «захватываю self, но слабенько, чтобы он мог сдохнуть, если захочет».

Пример 2: А можно и не только self

class Processor {
    var data: Data
    var onComplete: (() -> Void)?

    func process(with formatter: Formatter) {
        // Смотри, какой красивый захват: self — слабо, а formatter — сильно.
        someAsyncTask { [weak self, formatter] result in
            guard let self = self else { return } // self проверяем, жив ли ещё
            let output = formatter.format(result, using: self.data) // formatter тут как родной
            self.finish(with: output)
        }
    }
}

Вот тут capture list уже не только self ловит, но и formatter. Зачем? Да чтобы явно видеть, от кого замыкание зависит, а то потом ищешь, блядь, где у тебя retain cycle, а он в какой-нибудь неочевидной переменной затаился, как шпион в сортире.

Пример 3: Переименование, чтоб не путать

networkService.fetchData { [weak self] result in
    // Делаем сильную ссылку, но ТОЛЬКО локально, на время работы замыкания!
    guard let strongSelf = self else { return }
    strongSelf.handle(result)
    strongSelf.cleanup() // И всё безопасно, strongSelf не сбежит.
}

Это как взять друга за рукав, а потом сказать: «ладно, иди сюда, посидим нормально», но только внутри этой комнаты. На улицу он уже не выйдет, пока вы не закончите.

Правила, блядь, правила:

  1. Всегда пиши capture list для self, если есть хоть малейший шанс на retain cycle. Не будь самоубийцей, который думает: «да ладно, пронесёт».
  2. Другие переменные тоже захватывай явно, если они важны. Особенно делегаты, сервисы — всякую шелупонь, которая может испариться.
  3. weak self без capture list — это как пытаться открыть дверь без ключа, пинком. Не выйдет, компилятор тебя пошлёт нахуй. Capture list определяет, как захватить, а self? внутри — это уже использование того, что захватили.

Итог: Capture list — это не просто модная фича, а спасательный круг в море утечек памяти. А weak self — его главный поплавок. Используй, не ёби мозги себе и компилятору.