Ответ
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)
Ключевые сценарии использования:
- Последовательные цепочки задач: Например, «загрузить данные → распарсить → сохранить в кэш → обновить UI».
- Ограничение ресурсоемких операций: Например, не более 2 одновременных сетевых запросов для избежания throttling.
- Управляемая отмена: Пользователь покинул экран — отменяем все pending операции загрузки контента для этого экрана.
Недостаток: Небольшие накладные расходы по сравнению с простыми DispatchQueue.async блоками. Для тривиальных задач часто достаточно GCD.
Ответ 18+ 🔞
Ну вот, смотри, опять эти ваши очереди операций, OperationQueue, блядь. Как будто без них жизнь кончилась! А по сути — просто надстройка над GCD, чтобы не ебаться с низкоуровневыми потоками, как последний распиздяй.
Основные плюсы, за которые её так любят, ёпта:
- Зависимости между операциями. Можно сказать одной: «Слушай, сука, не начинай, пока вот эта не закончит». Порядок, блядь, а не хаос.
- Отмена. Нажал кнопку «назад» — и все твои фоновые загрузки, которые уже никому не нужны, можно послать нахуй одной командой.
operation.cancel(), и все дела. - Контроль над этим бардаком. Можешь сказать: «Так, больше трёх операций одновременно — ни-ни!».
maxConcurrentOperationCount— вот твой начальник цеха. - Состояние видно как на ладони. Готова, выполняется, закончила, отменили — всё прозрачно, не надо гадать.
- Приоритеты. Этой операции — все соки процессора, а эта пусть подождёт, невелика важность.
Вот тебе живой пример, как этим пользоваться, чтобы не вышло пиздеца:
Допустим, надо картинку скачать и потом на неё фильтр натянуть. Делаем две кастомные операции.
// 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 // Больше трёх загрузок одновременно — овердохуища, сервер сдохнет.
let loadOp = ImageLoadOperation(url: imageURL)
let filterOp = ImageFilterOperation()
// Вот она, магия зависимостей! Фильтр будет сидеть на жопе ровно, пока картинка не скачается.
filterOp.addDependency(loadOp)
// А результат загрузки передадим в фильтр через completionBlock, например.
filterOp.completionBlock = {
filterOp.inputImage = loadOp.image
}
// И вперёд, в очередь, блядь!
queue.addOperations([loadOp, filterOp], waitUntilFinished: false)
Где эта хуйня реально нужна:
- Цепочки задач, где один хер зависит от другого. Скачать → распарсить → сохранить → показать. Classic.
- Когда ресурсы надо беречь. Не больше двух сетевых запросов одновременно, а то баннер от провайдера получишь.
- Когда пользователь — нерешительная манда. Ушёл с экрана — все его фоновые операции отменяем к ебеням, чтобы память и батарейку не жрали.
Минус один, но жирный: На всё это есть свои накладные расходы, оверхеад, блядь. Для простой задачи, типа «выполни блок через секунду», не надо городить огород с Operation. Бери DispatchQueue.async и не парься. А вот для сложной логики — самое то, ёбана!