Если completion-блок хранится как свойство класса, должен ли он быть escaping?

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

Ответ

Да, такой блок должен быть помечен как @escaping.

По умолчанию в Swift параметры-функции являются non-escaping. Это означает, что они должны быть выполнены до завершения работы функции, в которую переданы. Если же блок сохраняется в свойство класса (или любой другой внешней переменной), его время жизни потенциально продлевается за рамки этой функции — он может быть вызван позже, асинхронно. Такой блок является escaping.

Пример, требующий @escaping:

class DataManager {
    var completionHandler: (() -> Void)? // Блок хранится как свойство

    func fetchData(completion: @escaping () -> Void) {
        // Сохраняем блок для вызова позже
        self.completionHandler = completion

        // Имитируем асинхронную операцию
        DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
            // Вызываем блок после завершения функции `fetchData`
            completion()
        }
    }
}

Если убрать @escaping, компилятор выдаст ошибку: "Escaping closure captures non-escaping parameter 'completion'", так как блок «сбегает» за пределы области вызова функции.