Планировщик задач Swift (iOS/macOS): GCD, OperationQueue и Timer
В разработке под iOS и macOS управление асинхронными задачами, отложенными выполнениями и многопоточностью — основа производительности и отзывчивости приложений. В Swift для этого используются три ключевых компонента: GCD (Grand Central Dispatch), OperationQueue и Timer. В этой статье мы подробно разберём каждый из них, покажем примеры кода и объясним, когда что применять.
1. Grand Central Dispatch (GCD) — DispatchQueue
GCD — это низкоуровневый C-API, который предоставляет Swift через DispatchQueue. Он позволяет выполнять задачи асинхронно, параллельно или последовательно, управляя потоками автоматически.
Основные типы очередей:
- Main Queue — последовательная очередь, выполняет задачи на главном потоке (UI).
- Global Queues — параллельные очереди с разными приоритетами (background, user-initiated, utility).
- Custom Serial/Concurrent Queues — создаются разработчиком.
Пример: асинхронная загрузка данных
import Foundation
// Получаем глобальную фоновую очередь
DispatchQueue.global(qos: .background).async {
// Тяжёлая работа (парсинг, загрузка)
let data = try? Data(contentsOf: URL(string: "https://example.com/data.json")!)
// Возвращаемся на главный поток для обновления UI
DispatchQueue.main.async {
// Обновляем интерфейс
print("Данные загружены: \(data?.count ?? 0) байт")
}
}
Отложенное выполнение (dispatch_after)
let deadline = DispatchTime.now() + 3.0 // через 3 секунды
DispatchQueue.main.asyncAfter(deadline: deadline) {
print("Прошло 3 секунды")
}
Группы задач (DispatchGroup)
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
// Задача 1
Thread.sleep(forTimeInterval: 2)
group.leave()
}
group.enter()
DispatchQueue.global().async {
// Задача 2
Thread.sleep(forTimeInterval: 1)
group.leave()
}
// Уведомление о завершении всех задач
group.notify(queue: .main) {
print("Все задачи завершены")
}
2. OperationQueue — высокоуровневый планировщик
OperationQueue построен поверх GCD, но предоставляет больше гибкости: можно задавать зависимости между операциями, приоритеты, отменять отдельные задачи, ограничивать максимальное количество параллельных операций.
Создание и выполнение операций
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2 // не более 2 параллельных задач
let operation1 = BlockOperation {
print("Операция 1")
Thread.sleep(forTimeInterval: 1)
}
let operation2 = BlockOperation {
print("Операция 2")
Thread.sleep(forTimeInterval: 1)
}
// Устанавливаем зависимость: operation2 начнётся только после operation1
operation2.addDependency(operation1)
queue.addOperations([operation1, operation2], waitUntilFinished: false)
Отмена операций
operation1.cancel() // Отменяет операцию, если она ещё не выполняется
// Проверка на отмену внутри операции
let cancellableOp = BlockOperation {
if !cancellableOp.isCancelled {
print("Выполняюсь")
}
}
Ключевые особенности OperationQueue:
- Можно задавать
qualityOfService(приоритет). - Операции могут быть объектами
Operationс переопределённым методомmain(). - Поддерживает KVO (Key-Value Observing) для отслеживания состояния.
3. Timer — для периодических и отложенных задач
Timer (ранее NSTimer) используется для выполнения кода через определённые интервалы. Важно: таймеры работают на RunLoop, поэтому для фоновых потоков нужно создавать свой RunLoop.
Пример: таймер с повторением
import Foundation
var timer: Timer?
func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
print("Тик: \(Date())")
}
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
Таймер на фоновом потоке
DispatchQueue.global().async {
let timer = Timer(timeInterval: 2.0, repeats: true) { _ in
print("Фоновый тик")
}
// Добавляем таймер в RunLoop текущего потока