Как определить, является ли замыкание escaping при использовании сторонней библиотеки в Swift?

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

Ответ

Определить тип замыкания (escaping/non-escaping) можно, анализируя сигнатуру метода библиотеки и логику его работы.

Основные способы определения:

  1. Аннотация @escaping: Самый прямой признак. Если параметр-замыкание в документации или заголовке API помечен как @escaping, оно является escaping.

    // Четко указано в сторонней библиотеке
    public func fetchUserProfile(completion: @escaping (Result<User, Error>) -> Void)
  2. Анализ контекста вызова (если аннотации нет): Нужно понять, может ли замыкание быть вызвано после возврата из функции. Ключевые индикаторы:

    • Замыкание сохраняется в свойство, глобальную переменную или массив.
    • Замыкание передается в другую функцию, параметр которой сам является @escaping (например, в DispatchQueue.async).
    • Функция запускает асинхронную операцию (сетевой запрос, таймер).
  3. Особенность Objective-C API: Все замыкания (blocks), приходящие из Objective-C API, по умолчанию считаются @escaping в Swift, так как компилятор не может гарантировать их синхронный вызов.

Практический пример анализа:

// Допустим, в документации библиотеки нет @escaping
func processData(with handler: (Data) -> Void)

// Чтобы понять, нужно посмотреть на реализацию или документацию.
// Если в ее теле есть код вида:
//   self.storedHandler = handler // Сохранение -> escaping
//   DispatchQueue.main.async { handler(data) } // Асинхронный вызов -> escaping
// То замыкание является escaping, даже если аннотации нет.

Рекомендация: Всегда проверяйте документацию. Если сомневаетесь, предполагайте @escaping для асинхронных API и избегайте сильных захватов ([weak self]) в замыканиях, которые могут жить долго.