0Pricing
Swift Academy · Урок

Убегающие замыкания

Сохраняйте замыкания, которые живут дольше вызвавшей их функции.

«Убегающие замыкания» — бесплатный урок 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 — локальная установка не требуется.

Все уроки этого курса

  1. Захват значений по ссылке
  2. Убегающие замыкания
  3. Списки захвата и weak self
  4. Автозамыкания
← Назад к Swift Academy