Планировщик задач Swift: GCD, OperationQueue и Combine для iOS/macOS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Планировщик задач 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)!                    

Рекомендаций