Ответ
Атрибут @escaping указывает, что переданное в функцию замыкание может быть вызвано после завершения работы этой функции. Это необходимо, когда замыкание сохраняется для асинхронного или отложенного выполнения.
Типичные сценарии использования:
- Асинхронные сетевые запросы.
- Анимации с завершением.
- Обработчики в GCD (
DispatchQueue). - Сохранение замыкания в свойстве класса.
Пример с сетевым запросом:
func loadUserProfile(for userId: String, completion: @escaping (Result<User, Error>) -> Void) {
let url = URL(string: "https://api.example.com/users/(userId)")!
// dataTask запускает асинхронную операцию и возвращает управление сразу.
URLSession.shared.dataTask(with: url) { data, response, error in
// Это замыкание выполняется ПОСЛЕ завершения `loadUserProfile`.
DispatchQueue.main.async {
if let error = error {
completion(.failure(error))
} else if let data = data {
// Декодирование и вызов completion
completion(.success(decodedUser))
}
}
}.resume()
// Функция loadUserProfile завершается здесь, но completion еще не вызван.
}
Ключевые отличия и правила:
| Не-escaping замыкание (по умолчанию) | Escaping замыкание (@escaping) |
|---|---|
| Должно быть выполнено до выхода из функции. | Может жить дольше, чем функция, которая его приняла. |
Не требует явного захвата self. |
Требует явного захвата ([weak self] или [unowned self]) для избежания циклов сильных ссылок. |
| Может быть оптимизировано компилятором. | Создает дополнительную нагрузку на память. |
Используйте @escaping только тогда, когда замыкание должно пережить контекст функции.