Что означает атрибут @escaping для замыкания в Swift?

«Что означает атрибут @escaping для замыкания в Swift?» — вопрос из категории Swift Core, который задают на 22% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Атрибут @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 только тогда, когда замыкание должно пережить контекст функции.