Когда замыкание в Swift может быть сбегающим, но не помечено как @escaping?

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

Ответ

Замыкание является сбегающим по семантике, но не требует аннотации @escaping, когда оно выполняется синхронно внутри вызываемой функции до её завершения. Компилятор Swift требует @escaping только если замыкание может быть вызвано после возврата из функции.

Пример без @escaping:

func performSync(closure: () -> Void) {
    // Замыкание выполняется немедленно, до выхода из функции
    closure()
}

performSync {
    print("Выполнено синхронно")
}

Контраст с @escaping:

class Storage {
    var savedClosure: (() -> Void)?

    func storeForLater(closure: @escaping () -> Void) {
        // Замыкание сохраняется и может быть вызвано позже
        savedClosure = closure
    }
}

Ключевое правило: @escaping требуется, когда замыкание:

  1. Сохраняется в свойство/переменную вне scope функции
  2. Передаётся в асинхронную операцию (DispatchQueue, URLSession)
  3. Возвращается как результат функции