Убегающие замыкания
Сохраняйте замыкания, которые живут дольше вызвавшей их функции.
«Убегающие замыкания» — бесплатный урок Swift Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Escaping и non-escaping
По умолчанию параметр closure является non-escaping: его необходимо вызвать до возврата функции. Пометьте его как @escaping, если он может быть сохранён и вызван позже.
Non-escaping closure
Этот closure выполняется синхронно и исчезает при возврате функции:
func run(_ work: () -> Void) {
work()
}
run { print("done now") } // "done now"Зачем нужен @escaping
Чтобы сохранить closure для последующего использования, требуется @escaping:
var handlers: [() -> Void] = []
func register(_ handler: @escaping () -> Void) {
handlers.append(handler) // escapes the function
}
register { print("later") }
handlers[0]() // "later"Escaping при асинхронной работе
Обработчики завершения являются escaping, потому что выполняются после возврата функции:
func load(then completion: @escaping (String) -> Void) {
// imagine async work
completion("data")
}
load { result in print(result) } // "data"self должен быть указан явно
Внутри escaping closure необходимо явно писать self. при обращении к членам экземпляра — это напоминает, что closure может удерживать self.
Явный self в escaping closure
Компилятор требует явной ссылки:
class Loader {
var name = "L"
func start(_ done: @escaping () -> Void) {
let cb = { print(self.name); done() }
cb()
}
}
Loader().start { print("finished") } // "L" then "finished"Хранение множества callback-функций
Escaping closures позволяют создавать списки наблюдателей:
class Emitter {
private var listeners: [(Int) -> Void] = []
func on(_ l: @escaping (Int) -> Void) { listeners.append(l) }
func fire(_ v: Int) { listeners.forEach { $0(v) } }
}
let e = Emitter()
e.on { print("got \($0)") }
e.fire(7) // "got 7"Возврат сохранённого closure
Closures, хранящиеся в свойствах, являются escaping:
class Button {
var action: (() -> Void)?
func setAction(_ a: @escaping () -> Void) { action = a }
}
let b = Button()
b.setAction { print("tapped") }
b.action?() // "tapped"Необязательные closures являются escaping
Необязательный параметр closure неявно является escaping (он обёрнут в Optional), поэтому его можно сохранить:
func maybe(_ f: (() -> Void)?) {
f?()
}
maybe { print("ran") } // "ran"Escaping в массиве задач
Очередь отложенной работы — классический пример использования escaping: каждый closure сохраняется, а затем выполняется позже:
var tasks: [() -> Int] = []
func schedule(_ task: @escaping () -> Int) {
tasks.append(task)
}
schedule { 1 + 1 }
schedule { 10 * 2 }
print(tasks.map { $0() }) // [2, 20]Замечание о производительности
Non-escaping closures можно агрессивнее оптимизировать (выделение памяти в куче не требуется). По возможности оставляйте closures non-escaping; делайте их escaping только тогда, когда действительно сохраняете или откладываете их.
func sum(_ xs: [Int], _ f: (Int) -> Int) -> Int {
xs.reduce(0) { $0 + f($1) } // non-escaping, fast
}
print(sum([1, 2, 3]) { $0 * $0 }) // 14Быстрая проверка
Когда параметр closure необходимо пометить как @escaping?
Итоги
Вы узнали об escaping closures:
- Non-escaping используется по умолчанию; closures выполняются до возврата функции
- Для сохранения или откладывания closure требуется
@escaping - Escaping closures требуют явного обращения к
self. - Необязательные параметры closures неявно являются escaping; для скорости предпочитайте non-escaping
Далее: списки захвата и weak self.
Часто задаваемые вопросы
Урок «Убегающие замыкания» бесплатный?
Да — полный текст урока «Убегающие замыкания» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Убегающие замыкания»?
Сохраняйте замыкания, которые живут дольше вызвавшей их функции. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Убегающие замыкания»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Захват значений по ссылке
- Убегающие замыкания
- Списки захвата и weak self
- Автозамыкания