Что такое non-escaping замыкание в Swift и когда оно используется?

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

Ответ

Non-escaping замыкание — это замыкание, которое гарантированно выполняется до завершения работы функции, в которую оно передано. Оно не сохраняется для использования после возврата из функции.

Ключевые характеристики:

  1. Выполняется синхронно в пределах тела функции.
  2. Не требует захвата self явно — компилятор знает, что замыкание не переживёт функцию.
  3. Оптимизировано для производительности — не требует дополнительного выделения памяти.

Пример non-escaping замыкания:

func processItems(_ items: [Int], using processor: (Int) -> Void) {
    for item in items {
        processor(item) // Выполняется немедленно
    }
    // Замыкание больше не существует
}

// Использование
processItems([1, 2, 3]) { number in
    print(number * 2) // Не нужно [weak self] или [unowned self]
}

Сравнение с escaping замыканием:

// NON-ESCAPING (по умолчанию)
func syncOperation(completion: () -> Void) {
    completion() // Всегда вызывается здесь
}

// ESCAPING (требует явного указания)
func asyncOperation(completion: @escaping () -> Void) {
    DispatchQueue.main.async {
        completion() // Вызывается ПОСЛЕ возврата из функции
    }
}

Правила использования:

  • По умолчанию все замыкания в параметрах функций являются non-escaping.
  • Добавляйте @escaping только если замыкание будет вызвано после возврата из функции.
  • В non-escaping замыканиях можно изменять захваченные переменные без inout.

Почему важно: Non-escaping замыкания безопаснее (нет риска retain cycles) и эффективнее, поэтому используются по умолчанию.