Очереди задач в Swift: GCD, OperationQueue и Combine для iOS/macOS

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

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

Начать курс

Очереди задач для Swift (iOS/macOS): Полное руководство

В разработке под iOS и macOS эффективное управление потоками и очередями задач — ключ к созданию отзывчивых и производительных приложений. Swift предлагает несколько мощных инструментов для работы с асинхронностью: от классического Grand Central Dispatch (GCD) до современной модели async/await и фреймворка Combine. В этой статье мы разберём основные подходы, их особенности и примеры использования.

1. Grand Central Dispatch (GCD) — основа многопоточности

GCD — это низкоуровневая библиотека Apple для управления параллельным выполнением кода. Основной элемент — DispatchQueue, который может быть последовательным (serial) или параллельным (concurrent).

Основные очереди:

  • main — главная очередь (UI-поток). Все обновления интерфейса должны выполняться на ней.
  • global — системные параллельные очереди с разным приоритетом (background, utility, default, userInitiated, userInteractive).
  • custom — пользовательские очереди, создаваемые через DispatchQueue(label:attributes:).

Пример: загрузка изображения в фоне

import UIKit

class ImageLoader {
    func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        DispatchQueue.global(qos: .background).async {
            guard let data = try? Data(contentsOf: url),
                  let image = UIImage(data: data) else {
                DispatchQueue.main.async {
                    completion(nil)
                }
                return
            }
            DispatchQueue.main.async {
                completion(image)
            }
        }
    }
}

2. OperationQueue — гибкое управление задачами

OperationQueue — более высокоуровневая абстракция над GCD. Позволяет создавать зависимости между операциями, устанавливать максимальное количество параллельных задач и отменять операции.

Ключевые возможности:

  • Создание BlockOperation или собственных подклассов Operation.
  • Установка зависимостей: operation2.addDependency(operation1).
  • Отмена всех операций: queue.cancelAllOperations().
  • Контроль качества обслуживания (quality of service).

Пример: последовательная загрузка файлов

import Foundation

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1 // последовательное выполнение

let fetchMetadata = BlockOperation {
    print("Загрузка метаданных...")
    Thread.sleep(forTimeInterval: 1)
}

let downloadContent = BlockOperation {
    print("Загрузка контента...")
    Thread.sleep(forTimeInterval: 2)
}

downloadContent.addDependency(fetchMetadata)

queue.addOperations([fetchMetadata, downloadContent], waitUntilFinished: false)
print("Операции поставлены в очередь")

3. Swift Concurrency (async/await) — современный подход

Начиная с Swift 5.5, Apple представила встроенную модель асинхронности с ключевыми словами async и await. Она позволяет писать асинхронный код линейно, без замыканий и callback-ов.

Основные компоненты:

  • async — маркировка асинхронной функции.
  • await — ожидание результата асинхронной операции.
  • Task — создание асинхронного контекста.
  • TaskGroup — группировка параллельных задач.

Пример: параллельная загрузка данных

import Foundation

func fetchUser(id: Int) async -> String {
    // Имитация сетевого запроса
    try? await Task.sleep(nanoseconds: 1_000_000_000)
    return "User_\(id)"
}

func loadAllUsers() async {
    await withTaskGroup(of: String.self) { group in
        for id in 1...5 {
            group.addTask {
                return await fetchUser(id: id)
            }
        }
        for await result in group {
            print("Загружен: \(result)")
        }
    }
}

// Запуск
Task {
    await loadAllUsers()
}

4. Combine — реактивные очереди

Combine — фреймворк для обработки событий во времени через издателей (Publisher) и подписчиков (Subscriber). Позволяет строить цепочки асинхронных операций с помощью операторов, таких как map, filter, flatMap.

Пример: обработка ввода с задержкой

import Combine

class SearchViewModel {
    @Published var query: String = ""
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $query
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .sink                    

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