Ответ
Замыкание является сбегающим по семантике, но не требует аннотации @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 требуется, когда замыкание:
- Сохраняется в свойство/переменную вне scope функции
- Передаётся в асинхронную операцию (DispatchQueue, URLSession)
- Возвращается как результат функции
Ответ 18+ 🔞
А, слушай, смотри, тут про замыкания в Swift один интересный момент есть, прямо как в анекдоте про немого Герасима — с виду одно, а на деле пиздец какая разница!
Вот представь: замыкание — это как обещание, которое ты даёшь функции. И компилятор, этот дотошный зануда, постоянно спрашивает: «Слушай, а ты это обещание собираешься выполнить прямо сейчас, до того как я отсюда выйду, или потом, когда я уже в жопу уйду?»
Если ты говоришь: «Да я щас, на месте, внатуре!» — то всё, спокойно, можно без всяких аннотаций. Это как сказать «Муму!» и сразу погладить собаку. Замыкание выполнилось синхронно, функция завершилась — все довольны.
func сделайСейчас(задание: () -> Void) {
// Щас, блядь, на месте! Прямо до того как отсюда выйти.
задание()
}
сделайСейчас {
print("Всё, готово, начальник!")
}
А вот если ты начинаешь хитрить, как тот самый злобный чувак из рассказа: «О, я это обещание сохраню на потом, в карман положу, а вызову его, когда ты уже за горизонтом будешь!» — вот тут-то компилятор тебя хватает за жабры и орет: «Так, стопэ, дружок-пирожок! Ты куда это? Это же @escaping нужно, ёпта! Ты ж его после меня вызывать собрался!»
class Хранилище {
var отложенноеОбещание: (() -> Void)? // Карман для обещаний
func сохранитьНаПотом(обещание: @escaping () -> Void) {
// Ага, сука, кладёшь в карман! Я тебя вижу!
отложенноеОбещание = обещание
}
}
И главное правило, которое надо выжечь на жопе, чтобы не забыть: @escaping нужно ТОЛЬКО в трёх случаях, когда замыкание:
- В карман положил — сохранил в свойство или переменную, которая живёт дольше функции. Пиздец, теперь оно сбежало!
- В асинхронную авантюру отправил — передал в
DispatchQueue,URLSessionили другую штуку, которая говорит «окей, сделаю, но не сейчас, а когда-нибудь». Типа «я потом, блядь, на озере утоплю». - Как подарочек вернул — функция возвращает это замыкание как результат. То есть ты его не выполнил, а выкинул наружу со словами «держи, разберись с этим сам».
А если ты его вызываешь прямо внутри, до того как сказать return — то всё, расслабься, это не сбегающее. Это как Герасим, который сразу говорит «Муму» и всё — никакой трагедии, собака жива, совесть чиста. Компилятор доволен, и ты не мудак.
Вот и вся философия, блядь. Либо делай сейчас, либо помечай @escaping и будь готов к последствиям, как тот самый максималист.