Ответ
Да, такой блок должен быть помечен как @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'", так как блок «сбегает» за пределы области вызова функции.
Ответ 18+ 🔞
А, ну это ж классика, блядь! Прямо как в том анекдоте про Герасима и Муму, только про замыкания.
Смотри, в Свифте эти блоки кода, они по умолчанию — как немой Герасим, пока ты с ним в одной комнате. Пока функция работает, он должен всё своё дело сделать и нахуй свалить. Не может он, сука, остаться жить у тебя в квартире после того, как ты дверь закрыл. Это non-escaping, блядь.
А вот если ты, хитрая жопа, решил этого Герасима-замыкание не отпускать, а пристроить куда-нибудь — в свойство класса записать, в массив сунуть, или отправить в асинхронную очередь типа DispatchQueue — то это уже совсем другая история, ёпта! Ты его продлеваешь жизнь за рамки функции. Он теперь escaping — сбежавший, блядь!
И компилятор, этот бдящий пидорас шерстяной, сразу начинает орать: «Ты куда это, мудак, non-escaping параметр пытаешься в вечность отправить? Он же тут должен отработать и сдохнуть!». И будет прав, сука.
Вот смотри на пример, тут всё как на ладони:
class DataManager {
var completionHandler: (() -> Void)? // Вот эта полка, куда мы Герасима сажаем
func fetchData(completion: @escaping () -> Void) {
// Сохраняем блок для вызова позже — вот он, момент побега!
self.completionHandler = completion
// Имитируем асинхронную операцию
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
// Вызываем блок после того, как функция `fetchData` уже давно кончилась!
completion() // А Герасим-то живой!
}
}
}
Видишь? Функция fetchData уже отработала, вернула управление, а этот ёбаный completion ещё сидит в очереди и ждёт своей секунды, чтобы выскочить и сказать «Муму!». Без пометки @escaping компилятор тебе мозги выест, потому что ты нарушаешь священный контракт, блядь.
Так что запомни: хочешь сохранить блок на потом — ставь @escaping. Не хочешь — не ставь, и пусть живёт только внутри функции. Всё просто, как ядрёна вошь.