Ответ
Escaping-замыкание — это замыкание, которое может быть вызвано после завершения выполнения функции, в которую оно было передано как аргумент. Для его обозначения используется атрибут @escaping.
Контраст с non-escaping (по умолчанию):
- Non-escaping: Компилятор гарантирует, что замыкание будет выполнено до выхода из функции. Это позволяет оптимизировать память (не требуется захватывать и хранить контекст).
- Escaping: Замыкание сохраняется (например, в свойстве, глобальной переменной или передается в другую асинхронную функцию) для последующего вызова. Это требует явного указания
@escaping.
Типичные сценарии использования:
- Асинхронные операции: Completion-обработчики для сетевых запросов, таймеров, анимаций.
- Хранение: Сохранение замыкания в свойстве класса для вызова позже (например, в обработчиках событий).
Пример с сетевым запросом:
func loadData(from url: URL, completion: @escaping (Result<Data, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
// Это замыкание выполняется асинхронно, ПОСЛЕ того как `loadData` вернула управление.
DispatchQueue.main.async {
if let error = error {
completion(.failure(error))
} else if let data = data {
completion(.success(data))
}
}
}
task.resume() // Функция завершается здесь, но `completion` еще не вызван.
}
Критически важные последствия:
- Циклы сильных ссылок (Retain Cycles): Поскольку escaping-замыкание переживает функцию, оно явно захватывает (
captures) используемые в нем объекты. Если замыкание, хранящееся в свойстве объекта, захватываетselfсильно (self.someProperty), возникает цикл удержания. - Обязательное использование weak/unowned: Для предотвращения циклов необходимо явно управлять захватом.
func setupHandler() { someAsyncOperation { [weak self] result in // Захватываем self слабой ссылкой! guard let self = self else { return } self.updateUI(with: result) } } - Изменяемость (
mutating): Escaping-замыкание не может захватывать изменяемую ссылкуselfвmutatingметоде структуры или перечисления.