← Назад к вопросам

Если completion-блок является полем класса, он escaping или non-escaping?

2.0 Middle🔥 121 комментариев
#Язык Swift

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Правовая природа completion-блока как поля класса

Основное правило

Если completion-блок хранится как свойство (поле) класса, то он автоматически становится escaping. Это фундаментальное правило Swift, связанное с семантикой захвата и временем жизни.

Почему это происходит?

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

  1. Время жизни класса обычно превышает время жизни функции.
  2. Свойство может быть использовано позже (например, в другом методе или после завершения функции).
class DataManager {
    // completion хранится как свойство -> автоматически escaping!
    private var completionHandler: (() -> Void)?

    func fetchData(completion: () -> Void) {
        // Здесь компилятор требует явно указать @escaping
        // потому что мы сохраняем блок в свойство
        self.completionHandler = completion
    }

    func processData() {
        completionHandler?() // Выполнение происходит позже
    }
}

Требования компилятора и безопасность

Swift компилятор строго контролирует эту ситуацию:

  • Если вы попытаетесь присвоить non-escaping параметр свойству класса, получите ошибку: "Non-escaping parameter 'completion' may only be called".
  • Для исправления нужно явно объявить параметр как @escaping:
func fetchData(completion: @escaping () -> Void) {
    self.completionHandler = completion // Теперь корректно
}

Практические следствия и рекомендации

1. Осознанное использование

Когда вы делаете блок escaping, вы должны учитывать:

  • Циклы захвата (retain cycles) — блок может захватить self, создавая сильную ссылку.
  • Управление памятью — нужно явно освобождать блок после использования.

2. Пример с захватом self

class DataManager {
    var completion: (() -> Void)?
    
    func loadData() {
        fetchData { [weak self] in
            // Используем weak self для предотвращения цикла
            self?.handleData()
        }
    }
}

3. Когда использовать такое хранилище?

  • Асинхронные операции — когда результат нужно обработать позже.
  • Состояние приложения — сохранение callback для будущих событий.
  • Делегирование через блоки — альтернатива протоколам.

Ключевые выводы

  • Свойство класса ↔ escaping — это неизбежная связь в Swift.
  • @escaping — обязательная аннотация при сохранении в свойство.
  • Безопасность памяти — главный фокус при работе с escaping блоками как свойствами.

Таким образом, ответ абсолютно четкий: если completion-блок становится полем класса, он всегда escaping, что требует соответствующего объявления в функции и особого внимания к управлению памятью.

Если completion-блок является полем класса, он escaping или non-escaping? | PrepBro