Ответ
Используйте NSOperation (и его подклассы) вместе с NSOperationQueue, когда требуется высокоуровневый контроль над выполнением задач, который GCD предоставляет не в полной мере.
Ключевые преимущества и сценарии:
- Зависимости между задачами. Можно явно указать, что одна операция должна начаться только после завершения других.
let downloadOp = BlockOperation { /* Загрузка данных */ } let parseOp = BlockOperation { /* Парсинг данных */ } parseOp.addDependency(downloadOp) // Парсинг ждет загрузку let queue = OperationQueue() queue.addOperations([downloadOp, parseOp], waitUntilFinished: false) - Управление состоянием и отмена. Операции имеют состояния (
isReady,isExecuting,isFinished,isCancelled). Поддерживается кооперативная отмена через методcancel(). - Контроль параллелизма. Легко ограничить количество одновременно выполняемых операций в очереди.
queue.maxConcurrentOperationCount = 2 // Не более 2 операций параллельно - Приоритеты. Установка приоритета выполнения (
queuePriority). - Переиспользование и инкапсуляция. Сложную логику можно инкапсулировать в кастомный подкласс
Operation.
Когда выбрать GCD (DispatchQueue):
- Для простых асинхронных или отложенных задач.
- Когда достаточно сериальных или параллельных очередей без сложных зависимостей.
- Для низкоуровневой синхронизации (семафоры, барьеры).
NSOperationQueue часто реализован поверх GCD и предоставляет более удобный объектно-ориентированный API для сложных рабочих потоков.
Ответ 18+ 🔞
А, слушай, вот эта тема про NSOperation и DispatchQueue — это как выбрать между молотком и шуруповёртом, когда гвозди уже все забили, а надо что-то посерьёзнее собрать.
Смотри, если тебе надо не просто «эй, выполни это где-нибудь на фоне», а чтоб всё было по чёткому плану, с зависимостями и возможностью всё отменить одним махом — тут тебе не DispatchQueue, а его старший, более навороченный братан, NSOperationQueue.
Чем он, блядь, так хорош, спросишь?
-
Зависимости, ёпта! Это ж главный козырь. Можно сказать одной операции: «Слушай, дура, не начинай, пока вот эта падла не закончит своё дело». Красота же!
let downloadOp = BlockOperation { /* Качаю данные, бля */ } let parseOp = BlockOperation { /* А вот это уже парсю */ } parseOp.addDependency(downloadOp) // Парсить буду ТОЛЬКО после загрузки, никак иначе! let queue = OperationQueue() queue.addOperations([downloadOp, parseOp], waitUntilFinished: false)Без этого пришлось бы городить костыли из семафоров или барьеров в GCD. А тут — одна строчка, и порядок.
-
Отмена, мать её! Операцию можно просто
cancel()нуть. И если она написана не кривыми руками, она кооперативно завершится, проверив флагisCancelled. В GCD с этим сложнее — там либо отпускай задачу в свободное плавание, либо выкручивайся сам. -
Ограничение потока. Хочешь, чтобы твои 15 операций не сожрали все ядра процессора? Легко!
queue.maxConcurrentOperationCount = 2 // Больше двух не влезай, очередь, блядь!Сделал — и спи спокойно, никакой самодеятельности.
-
Приоритеты и кастомная логика. Можно выставить
queuePriority. А если задача вообще хитрая, как жопа с ручками, то пишешь свой собственный подклассOperation, инкапсулируешь туда всю логику и потом юзаешь его как чёрный ящик. Красота, а не жизнь.
А когда тогда брать старый добрый GCD (DispatchQueue)?
Да когда всё просто, нахуй! Кинул клоужер в async, получил результат в main — и свободен. Или нужно поставить барьер, чтоб синхронизировать доступ к ресурсу. Или использовать семафоры для тонкой настройки. GCD — это как отвёртка: быстрая, простая, для базовых задач.
А NSOperationQueue — это уже целый набор инструментов, который, кстати, часто внутри себя использует тот же GCD. Но даёт тебе такую обёртку и контроль, что просто волосы дыбом встают от удобства для сложных сценариев.
Короче, если задача простая — GCD и не парься. Если начинаешь чувствовать, что код превращается в спагетти из колбэков и семафоров — это верный знак, что пора пересаживаться на NSOperationQueue. Он хоть и выглядит посерьёзнее, но в итоге спасает от ебалы головной.