Планировщик задач Swift (iOS/macOS): Полное руководство
Планировщик задач (Task Scheduler) — ключевой компонент разработки под iOS и macOS, позволяющий эффективно управлять асинхронными операциями, фоновыми задачами и многопоточностью. В Swift доступно несколько мощных инструментов: Grand Central Dispatch (GCD), OperationQueue, новый механизм async/await (Swift Concurrency) и фреймворк Combine. В этой статье мы подробно разберём каждый из них, приведём примеры кода и рекомендации по выбору подходящего инструмента.
1. Grand Central Dispatch (GCD)
GCD — низкоуровневая библиотека для управления параллельными задачами. Она работает с очередями (DispatchQueue) и обеспечивает эффективное распределение потоков.
Основные типы очередей:
- main — главная очередь (UI-поток).
- global — фоновые очереди с различными приоритетами (QoS).
- private — пользовательские очереди (serial или concurrent).
Пример использования GCD:
import Foundation
// Асинхронная задача в фоне
DispatchQueue.global(qos: .background).async {
let result = performHeavyComputation()
// Возвращаемся на главный поток для обновления UI
DispatchQueue.main.async {
updateUI(with: result)
}
}
// Серийная очередь для последовательного выполнения
let serialQueue = DispatchQueue(label: "com.example.serial")
serialQueue.async {
// Задача 1
}
serialQueue.async {
// Задача 2 (выполнится после задачи 1)
}
// Параллельная очередь
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
concurrentQueue.sync {
// Блокирующая операция (осторожно!)
}
2. OperationQueue
OperationQueue — более высокоуровневая абстракция над GCD. Она работает с объектами Operation, которые можно настраивать, отменять и связывать зависимостями.
Преимущества OperationQueue:
- Гибкое управление зависимостями между задачами.
- Встроенная поддержка отмены и приоритетов.
- KVO-наблюдение за состоянием операций.
Пример с BlockOperation:
import Foundation
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 2
let operation1 = BlockOperation {
print("Задача 1")
}
let operation2 = BlockOperation {
print("Задача 2")
}
let operation3 = BlockOperation {
print("Задача 3")
}
// Устанавливаем зависимость: задача 3 выполнится после задачи 1 и 2
operation3.addDependency(operation1)
operation3.addDependency(operation2)
operationQueue.addOperations([operation1, operation2, operation3], waitUntilFinished: false)
// Отмена всех операций
operationQueue.cancelAllOperations()
Создание собственного подкласса Operation:
class DownloadOperation: Operation {
private let url: URL
init(url: URL) {
self.url = url
}
override func main() {
guard !isCancelled else { return }
// Выполняем загрузку
let data = try? Data(contentsOf: url)
// Обработка результата
}
}
let downloadOp = DownloadOperation(url: URL(string: "https://example.com/data")!)
operationQueue.addOperation(downloadOp)
3. Swift Concurrency (async/await)
Начиная с Swift 5.5, появилась встроенная поддержка асинхронного программирования через ключевые слова async, await и Task. Это современный и безопасный способ работы с конкурентностью.
Основные компоненты:
- async — маркировка асинхронной функции.
- await — ожидание завершения асинхронной операции.
- Task — создание новой асинхронной задачи.
- TaskGroup — групповое выполнение задач.
Пример async/await:
import Foundation
// Асинхронная функция
func fetchUserData() async throws -> User {
let url = URL(string: "https://api.example.com/user")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
// Вызов в Task
Task {
do {
let user = try await fetchUserData()
await MainActor.run {
// Обновление UI на главном потоке
self.userLabel.text = user.name
}
} catch {
print("Ошибка: \(error)")
}
}
// Параллельные задачи с TaskGroup
func loadImages(urls: [URL]) async throws -> [UIImage] {
try await withThrowingTaskGroup(of: UIImage.self) { group in
for url in urls {
group.addTask {
let (data, _) = try await URLSession.shared.data(from: url)
return UIImage(data: data)!