Connect with us

Программирование

Defer в Swift: уберитесь перед уходом

Ключевое слово defer не является чем-то, что нужно каждый день, но это отличный инструмент для того, чтобы сделать код очистки более безопасным и понятным.

Опубликовано

/

     
     

Возможно, вы считаете ключевое слово defer одной из самых неоднозначных особенностей языка Swift, но в некоторых случаях оно очень полезно. Его можно использовать целенаправленно, и это обеспечит вам безопасность. На этой неделе мы поговорим о некоторых лучших практиках использования defer в Swift.

Ключевое слово defer в Swift позволяет выполнить блок кода в конце текущего скоупа. Что означает текущий скоуп (область видимости)? Обычно это ближайшая пара фигурных скобок. Давайте рассмотрим несколько примеров.

func foo() {
    // start parent scope

    for i in 0...10 {
        // start scope i
        print(i)
        // end scope i
    }
    // end parent scope
}

Как видно из приведенного выше примера, каждая открывающая фигурная скобка определяет новую область видимости. Это может быть функция, цикл for, вычисляемое свойство и т. д. Таким образом, ключевое слово defer, определенное внутри определенной области видимости, влияет только на эту область видимости и выполняется непосредственно перед ее концом.

func foo() {
    // start parent scope
    defer {
        print("end parent scope")
    }

    for i in 0...10 {
        // start scope i
        defer {
            print("end scope, \(i)")
        }

        print(i)
        // end scope i
    }
    // end parent scope, print("end parent scope") runs here
}

Здесь я пытаюсь показать, где должны выполняться блоки отложенного выполнения. Каждая закрывающая фигурная скобка закрывает область видимости, и именно здесь выполняются блоки отложенного выполнения defer.

Таким образом, вы можете разместить блоки отложенного выполнения в любом месте области видимости, но они будут выполняться в конце этой области. Зачем нам такое неоднозначное поведение? Мы привыкли читать и понимать код построчно, но блоки отложенного выполнения — это другое дело. Я уверен, что вам не часто приходится использовать блоки отложенного выполнения, но есть случаи, когда они оказываются полезными.

func fetch(_ epg: URL) async throws {
    let uuid = UUID()
    let (data, _) = try await URLSession.shared.data(from: epg)
    let url = URL.temporaryDirectory.appending(path: uuid.uuidString)
    try data.write(to: url)

    guard let parser = XMLParser(contentsOf: url) else {
        return
    }

    parser.parse()

    try FileManager.default.removeItem(at: url)
}

Как видите, мы создаём файл во временном каталоге, и его следует удалить, как только мы закончим с ним работу. Вы можете создать этот файл и легко забыть его удалить. Поверьте, такое случалось чаще, чем вы думаете.

func fetch(_ epg: URL) async throws {
    let uuid = UUID()
    let (data, _) = try await URLSession.shared.data(from: epg)
    let url = URL.temporaryDirectory.appending(path: uuid.uuidString)
    try data.write(to: url)
    
    defer {
        try? FileManager.default.removeItem(at: url)
    }

    guard let parser = XMLParser(contentsOf: url) else {
        return
    }

    parser.parse()
}

Вы можете создать файл и определить блок defer, который его удалит. Вы можете разместить его непосредственно после создания файла, но он будет выполнен в конце области видимости.

for await handler in await health.observe(
    types: [
        .workoutType(),
        HKQuantityType.hrv,
        HKQuantityType.heartRate,
        HKCategoryType.sleep,
        HKCategoryType.mindful
    ]
) {
    defer { handler() }
    
    if handler.types.contains(HKQuantityType.heartRate) {
        await processHeartRate()
    }
}

Позвольте показать вам ещё один пример. Здесь мы используем API наблюдений HealthKit, который требует вызова completion handler сразу после завершения обработки. Это ещё одно интересное применение блока defer, поскольку вы определяете его в начале области видимости, но он будет выполнен в конце, сразу после завершения всей обработки.

Ключевое слово defer не является чем-то, что нужно каждый день, но это отличный инструмент для того, чтобы сделать код очистки более безопасным и понятным. Всякий раз, когда вы создаёте временный ресурс, получаете блокировку, изменяете какое-либо состояние или получаете обработчик завершения, который должен быть вызван позже, defer позволяет описать очистку прямо рядом с настройкой. Надеюсь, вам понравилась статья.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: