> Будет ли ошибка при передаче non-escaping closure дальше и почему? (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Doubletapp

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Да, будет ошибка компиляции. Non-escaping closure нельзя передавать дальше - в другую функцию, сохранять в свойство или захватывать внутри другой escaping closure. Это ограничение существует потому, что non-escaping closure гарантированно выполняется синхронно внутри тела функции, и компилятор может оптимизировать память, не сохраняя closure в куче. Передача дальше нарушила бы эту гарантию и потребовала бы heap allocation, что противоречит контракту non-escaping.

Подробное объяснение

В Swift параметры-замыкания по умолчанию являются non-escaping. Это означает, что closure не может "пережить" вызов функции - он должен быть вызван до возврата из функции. Компилятор использует это знание для оптимизации: non-escaping closure не требует захвата контекста в куче, он может быть аллоцирован на стеке.

Когда вы пытаетесь передать non-escaping closure в другую функцию, компилятор выдаёт ошибку: "escaping closure captures non-escaping parameter". Причина в том, что принимающая функция может сохранить closure или выполнить его асинхронно, что нарушит гарантию времени жизни.

Исключение: если принимающая функция также объявлена как non-escaping и вызывает closure синхронно до своего возврата, передача допустима. Например:

SWIFT
func outer(_ closure: () -> Void) {
inner(closure) // допустимо, если inner тоже non-escaping
}
func inner(_ closure: () -> Void) {
closure()
}

Но если принимающая функция объявлена как @escaping, передача non-escaping closure в неё - ошибка.

На практике

В реальном коде это ограничение часто встречается при работе с async/await, GCD, Combine или любыми API, которые хранят замыкания для последующего вызова. Типичный сценарий - попытка передать замыкание в DispatchQueue.async или в метод, который сохраняет его в свойстве.

Решение: пометить параметр как @escaping, если вы планируете передавать его дальше или хранить. Но помните, что escaping closure всегда аллоцируется в куче, что добавляет небольшие накладные расходы. Поэтому не стоит делать все замыкания escaping без необходимости.

Также важно понимать: если closure захватывает self и является escaping, нужно явно указывать [weak self] или [unowned self], чтобы избежать retain cycle.

Пример кода

SWIFT
// Ошибка: escaping closure captures non-escaping parameter
func process(_ completion: () -> Void) {
DispatchQueue.main.async {
completion() // ❌ Cannot pass non-escaping parameter to escaping closure
}
}
// Правильно: пометить параметр как @escaping
func process(_ completion: @escaping () -> Void) {
DispatchQueue.main.async {
completion() // ✅
}
}
// Допустимо: передача в non-escaping функцию
func outer(_ closure: () -> Void) {
inner(closure) // ✅
}
func inner(_ closure: () -> Void) {
closure()
}

Как отвечать на собеседовании

Начните с чёткого ответа: "Да, ошибка будет". Затем объясните причину: non-escaping closure гарантирует синхронное выполнение, компилятор не выделяет память в куче, а передача дальше нарушает эту гарантию. Упомяните, что исключение - передача в другую non-escaping функцию, которая вызывает closure синхронно. Покажите пример с @escaping как решением. Если спросят про оптимизацию, скажите, что non-escaping closure может быть аллоцирован на стеке, а escaping - только в куче.

Что проверяет интервьюер

Интервьюер проверяет понимание модели памяти Swift, различий между escaping и non-escaping замыканиями, а также способность объяснить, почему компилятор накладывает такие ограничения. Это вопрос на уровень senior, потому что требует знания не только синтаксиса, но и внутренних механизмов компилятора и оптимизаций.

Типичные ошибки

  • Ответ "ошибки не будет" - грубая ошибка.
  • Объяснение только через синтаксис, без упоминания heap/stack и гарантий времени жизни.
  • Утверждение, что non-escaping closure нельзя передавать никуда вообще - забывают про передачу в другую non-escaping функцию.
  • Путаница между @escaping и @autoclosure - это разные вещи.
  • Неупоминание retain cycle при использовании @escaping с self.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью