Как использовать dispatch_after в Swift 3, 4 и 5 для отложенного выполнения?
Правильный способ отложенного выполнения в Swift 3+
В Swift 3 API Grand Central Dispatch (GCD) был полностью переработан. Вместо старого C-стиля dispatch_after теперь используется современный синтаксис с методами asyncAfter у DispatchQueue. Вот как это выглядит:
// Базовая задержка на 2 секунды на главном потоке
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Прошло 2 секунды")
}
Объяснение синтаксиса
- DispatchQueue.main — очередь, на которой будет выполняться код (главный поток для UI).
- asyncAfter(deadline: .now() + 2.0) — метод, который запускает замыкание через указанное время.
- .now() — текущий момент времени.
- + 2.0 — добавляем 2 секунды к текущему времени (можно использовать Double, Int или DispatchTimeInterval).
Примеры для разных сценариев
1. Задержка на главном потоке (для обновления UI)
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.label.text = "Текст появился через 3 секунды"
}
2. Задержка на фоновой очереди
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 1.5) {
// Тяжелая работа в фоне
let result = self.performHeavyTask()
DispatchQueue.main.async {
// Возвращаем результат на главный поток
self.updateUI(with: result)
}
}
3. Использование DispatchTimeInterval для наглядности
let delay = DispatchTimeInterval.seconds(2)
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
print("Задержка 2 секунды через DispatchTimeInterval")
}
// Или для миллисекунд:
let msDelay = DispatchTimeInterval.milliseconds(500)
DispatchQueue.main.asyncAfter(deadline: .now() + msDelay) {
print("Задержка 500 мс")
}
4. Отмена запланированной задачи (если нужно)
Для отмены используйте DispatchWorkItem:
let workItem = DispatchWorkItem {
print("Этот блок может быть отменён")
}
// Запланировать выполнение через 5 секунд
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0, execute: workItem)
// Если передумали — отменяем
workItem.cancel()
Почему это работает во всех версиях Swift 3, 4, 5?
API asyncAfter был введён в Swift 3 и остаётся неизменным в Swift 4 и 5. Единственное, что могло измениться — это синтаксис замыканий (trailing closure), но пример выше совместим со всеми версиями. Если вы используете Swift 5.5+ и iOS 13+, можно также рассмотреть Task.sleep из async/await, но GCD остаётся стандартным решением для простых задержек.
Полный рабочий пример для копирования
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Задержка 2 секунды, затем обновление UI
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
guard let self = self else { return }
self.view.backgroundColor = .red
print("Фон стал красным через 2 секунды")
}
}
}
Не забудьте использовать [weak self] внутри замыкания, если оно захватывает self, чтобы избежать retain cycle.
Альтернативный подход: использование Timer
Если вам нужно выполнить однократное действие с задержкой и при этом иметь возможность отменить его, можно использовать Timer (ранее NSTimer). Этот способ может быть удобнее, если вы уже работаете с таймерами в проекте:
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { timer in
print("Сработал таймер через 2 секунды")
// Здесь можно обновить UI
}
Преимущества Timer:
- Простая отмена через
timer.invalidate() - Легко превратить в повторяющийся (repeats: true)
- Работает на главном потоке по умолчанию
Недостатки:
- Таймер менее точен при больших задержках (может "плавать" на загруженном RunLoop)
- Требует явного управления жизненным циклом (слабая ссылка на self)
Для Swift 5.5+ (iOS 13+) — async/await
Если ваш проект использует современный Swift Concurrency, можно сделать задержку через Task:
Task {
try? await Task.sleep(nanoseconds: 2_000_000_000) // 2 секунды
print("Прошло 2 секунды (async/await)")
}
Этот способ наиболее читаемый, но требует iOS 13+ и Swift 5.5. Для обратной совместимости GCD остаётся лучшим выбором.
Ответов пока нет.
Войдите чтобы оставить ответ