Ответ
Capture list (список захвата) — это синтаксическая конструкция в начале замыкания, которая явно определяет как переменные из окружающего контекста захватываются и хранятся внутри замыкания. Это основной инструмент для предотвращения retain cycles (циклов сильных ссылок).
Проблема (retain cycle):
class MyViewController {
var closure: (() -> Void)?
func problematicSetup() {
closure = {
self.doSomething() // Сильный захват `self` -> ЦИКЛ!
}
}
// `self` удерживает `closure`, а `closure` удерживает `self`.
}
Решение с использованием capture list:
func correctSetup() {
// [weak self] создает слабую (optional) ссылку
closure = { [weak self] in
self?.doSomething() // `self` стал Optional
}
// [unowned self] создает unowned (non-optional, но небезопасную) ссылку
closure = { [unowned self] in
self.doSomething() // Краш, если `self` освобожден
}
}
Ключевые правила выбора:
- Используйте
[weak capturedInstance], если захваченный объект может статьnil(наиболее безопасный и распространенный способ). - Используйте
[unowned capturedInstance], только если время жизни объекта гарантированно превышает время жизни замыкания (редкие случаи, риск краша). - Можно захватывать значения по значению:
[x, y]— создается локальная копия. - Можно давать новые имена:
[weak weakSelf = self].
Пример с несколькими захватами:
networkService.fetchData { [weak self, unowned database, count = totalAttempts] result in
guard let self = self else { return }
self.handle(result, from: database)
print("Attempt: (count)")
}
Зачем это нужно: Без правильного capture list замыкания, захватывающие self или другие ссылочные типы, могут создавать циклы удержания памяти, приводящие к утечкам.