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

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

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

Начать курс

Планировщик задач 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 текущего потока                    

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