В чем разница между GCD (Grand Central Dispatch) и Operation/OperationQueue?

«В чем разница между GCD (Grand Central Dispatch) и Operation/OperationQueue?» — вопрос из категории Многопоточность, который задают на 23% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

GCD и Operation — это API для многопоточности в iOS/macOS, но разного уровня абстракции.

Характеристика GCD (Grand Central Dispatch) Operation / OperationQueue
Уровень Низкоуровневый C-API (через libdispatch). Высокоуровневая объектно-ориентированная абстракция над GCD.
Единица работы Замыкание (closure/block), отправляемое в очередь (DispatchQueue). Объект (Operation — абстрактный класс, BlockOperation — конкретный).
Состояние Нет явного состояния задачи. Есть свойства (isReady, isExecuting, isFinished, isCancelled). KVO-совместимо.
Зависимости Нет встроенной поддержки. Реализуется вручную (через барьеры, семафоры, группы). Есть встроенная поддержка. operationB.addDependency(operationA).
Отмена Нет встроенной отмены отправленной задачи. Есть встроенная отмена. operation.cancel() (кооперативная).
Приоритет Качество обслуживания (qos: .userInteractive, .background и т.д.). queuePriority (в очереди) и qualityOfService.
Контроль Ограниченный. Отправил задачу — управление передано системе. Полный. Можно переиспользовать, наблюдать за состоянием, паузировать очередь.

Пример GCD:

// Простая фоновая задача с обновлением UI
DispatchQueue.global(qos: .userInitiated).async {
    let image = self.loadImageFromNetwork() // Тяжелая работа
    DispatchQueue.main.async {
        self.imageView.image = image // Обновление UI в главном потоке
    }
}

Пример Operation:

// Создание зависимых операций с отменой
let downloadOp = BlockOperation {
    // Загрузка данных
}
let processOp = BlockOperation {
    // Обработка данных
}
let updateUIOp = BlockOperation {
    DispatchQueue.main.async { /* Обновить UI */ }
}

// Настройка зависимостей: обработка -> после загрузки, UI -> после обработки
processOp.addDependency(downloadOp)
updateUIOp.addDependency(processOp)

// Добавление в очередь (запуск)
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1 // Серийная очередь для примера
queue.addOperations([downloadOp, processOp, updateUIOp], waitUntilFinished: false)

// Возможность отменить все
queue.cancelAllOperations()

Когда что использовать:

  • GCD: Для простых, разовых, независимых задач, где важна минимальная производительность (например, асинхронный вызов с возвратом на главный поток).
  • Operation: Для сложных, связанных задач, требующих управления зависимостями, отмены, повторного использования или наблюдения за состоянием (например, цепочка загрузки и обработки данных).