Каков твой опыт работы с OperationQueue в iOS?

«Каков твой опыт работы с OperationQueue в iOS?» — вопрос из категории Многопоточность, который задают на 22% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

OperationQueue — это высокоуровневая абстракция над GCD (Grand Central Dispatch) для управления очередями задач, представленных объектами Operation. Основные преимущества перед «чистым» GCD:

  • Зависимости между операциями: Можно четко указать порядок выполнения.
  • Отмена операций: Поддержка отмены как отдельных операций (operation.cancel()), так и всей очереди.
  • Контроль параллелизма: Установка максимального количества одновременно выполняемых операций (maxConcurrentOperationCount).
  • Состояние операции: Возможность отслеживать isReady, isExecuting, isFinished, isCancelled.
  • Приоритеты: Настройка queuePriority и qualityOfService.

Пример: Загрузка и обработка изображения с зависимостями.

// 1. Создание кастомной операции для загрузки
class ImageLoadOperation: Operation {
    let url: URL
    var image: UIImage?
    init(url: URL) { self.url = url }
    override func main() {
        guard !isCancelled else { return }
        let data = try? Data(contentsOf: url) // В реальности — асинхронный запрос
        image = UIImage(data: data ?? Data())
    }
}

// 2. Создание операции для обработки (например, применения фильтра)
class ImageFilterOperation: Operation {
    var inputImage: UIImage?
    var outputImage: UIImage?
    override func main() {
        guard let input = inputImage, !isCancelled else { return }
        // Применяем фильтр...
        outputImage = applySepiaFilter(to: input)
    }
}

// 3. Настройка очереди и зависимостей
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 3 // Не более 3 параллельных загрузок

let loadOp = ImageLoadOperation(url: imageURL)
let filterOp = ImageFilterOperation()

// Устанавливаем зависимость: фильтрация только после загрузки
filterOp.addDependency(loadOp)
// Передаем результат загрузки в операцию фильтрации
filterOp.completionBlock = {
    filterOp.inputImage = loadOp.image
}

queue.addOperations([loadOp, filterOp], waitUntilFinished: false)

Ключевые сценарии использования:

  1. Последовательные цепочки задач: Например, «загрузить данные → распарсить → сохранить в кэш → обновить UI».
  2. Ограничение ресурсоемких операций: Например, не более 2 одновременных сетевых запросов для избежания throttling.
  3. Управляемая отмена: Пользователь покинул экран — отменяем все pending операции загрузки контента для этого экрана.

Недостаток: Небольшие накладные расходы по сравнению с простыми DispatchQueue.async блоками. Для тривиальных задач часто достаточно GCD.