Ответ
Замыкания захватывают переменные из окружающей области видимости для использования внутри своего тела. По умолчанию захват происходит по сильной ссылке, что может создавать циклы удержания (retain cycles).
Типы захвата:
1. Захват по сильной ссылке (по умолчанию):
class DataManager {
var data: String = ""
lazy var processor: () -> Void = {
print(self.data) // Сильный захват self
}
}
// Цикл удержания: DataManager ←→ processor
2. Захват по слабой ссылке (weak):
class DataManager {
var data: String = ""
lazy var processor: () -> Void = { [weak self] in
guard let self = self else { return }
print(self.data) // Временная сильная ссылка
}
}
// Нет цикла удержания
3. Захват по бесхозной ссылке (unowned):
class Serializer {
let format: String
lazy var description: () -> String = { [unowned self] in
return "Serializer with format: (self.format)"
}
// Используется, когда self гарантированно существует
}
4. Захват value types по значению:
var counter = 0
let closure = { [counter] in // Захват копии значения
print(counter) // Всегда 0, даже если внешний counter изменится
}
counter = 10
closure() // Выводит: 0
Списки захвата (Capture Lists): Списки захвата определяются в квадратных скобках перед списком параметров:
{ [weak viewController, unowned database, count = self.count] in
// viewController — weak optional
// database — unowned (не optional)
// count — копия self.count
}
Правила захвата:
- Reference types всегда захватываются по ссылке (сильной, слабой или бесхозной)
- Value types захватываются по значению (копия), если не указано иное
- self в методах экземпляра захватывается неявно при обращении к свойствам
- Захват происходит при создании замыкания, а не при его вызове
Пример с асинхронным кодом:
func fetchData(completion: @escaping (Result) -> Void) {
let requestId = generateId() // Value type
networkService.request { [weak self] result in
// Захватываем weak self чтобы избежать цикла
// requestId захвачен по значению (копия)
self?.handle(result, requestId: requestId)
completion(result)
}
}
Правильное управление захватами критически важно для предотвращения утечек памяти в Swift.