Когда использовать NSOperation и NSOperationQueue в iOS?

Ответ

Используйте NSOperation (и его подклассы) вместе с NSOperationQueue, когда требуется высокоуровневый контроль над выполнением задач, который GCD предоставляет не в полной мере.

Ключевые преимущества и сценарии:

  1. Зависимости между задачами. Можно явно указать, что одна операция должна начаться только после завершения других.
    let downloadOp = BlockOperation { /* Загрузка данных */ }
    let parseOp = BlockOperation { /* Парсинг данных */ }
    parseOp.addDependency(downloadOp) // Парсинг ждет загрузку
    let queue = OperationQueue()
    queue.addOperations([downloadOp, parseOp], waitUntilFinished: false)
  2. Управление состоянием и отмена. Операции имеют состояния (isReady, isExecuting, isFinished, isCancelled). Поддерживается кооперативная отмена через метод cancel().
  3. Контроль параллелизма. Легко ограничить количество одновременно выполняемых операций в очереди.
    queue.maxConcurrentOperationCount = 2 // Не более 2 операций параллельно
  4. Приоритеты. Установка приоритета выполнения (queuePriority).
  5. Переиспользование и инкапсуляция. Сложную логику можно инкапсулировать в кастомный подкласс Operation.

Когда выбрать GCD (DispatchQueue):

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

NSOperationQueue часто реализован поверх GCD и предоставляет более удобный объектно-ориентированный API для сложных рабочих потоков.

Ответ 18+ 🔞

А, слушай, вот эта тема про NSOperation и DispatchQueue — это как выбрать между молотком и шуруповёртом, когда гвозди уже все забили, а надо что-то посерьёзнее собрать.

Смотри, если тебе надо не просто «эй, выполни это где-нибудь на фоне», а чтоб всё было по чёткому плану, с зависимостями и возможностью всё отменить одним махом — тут тебе не DispatchQueue, а его старший, более навороченный братан, NSOperationQueue.

Чем он, блядь, так хорош, спросишь?

  1. Зависимости, ёпта! Это ж главный козырь. Можно сказать одной операции: «Слушай, дура, не начинай, пока вот эта падла не закончит своё дело». Красота же!

    let downloadOp = BlockOperation { /* Качаю данные, бля */ }
    let parseOp = BlockOperation { /* А вот это уже парсю */ }
    parseOp.addDependency(downloadOp) // Парсить буду ТОЛЬКО после загрузки, никак иначе!
    let queue = OperationQueue()
    queue.addOperations([downloadOp, parseOp], waitUntilFinished: false)

    Без этого пришлось бы городить костыли из семафоров или барьеров в GCD. А тут — одна строчка, и порядок.

  2. Отмена, мать её! Операцию можно просто cancel()нуть. И если она написана не кривыми руками, она кооперативно завершится, проверив флаг isCancelled. В GCD с этим сложнее — там либо отпускай задачу в свободное плавание, либо выкручивайся сам.

  3. Ограничение потока. Хочешь, чтобы твои 15 операций не сожрали все ядра процессора? Легко!

    queue.maxConcurrentOperationCount = 2 // Больше двух не влезай, очередь, блядь!

    Сделал — и спи спокойно, никакой самодеятельности.

  4. Приоритеты и кастомная логика. Можно выставить queuePriority. А если задача вообще хитрая, как жопа с ручками, то пишешь свой собственный подкласс Operation, инкапсулируешь туда всю логику и потом юзаешь его как чёрный ящик. Красота, а не жизнь.

А когда тогда брать старый добрый GCD (DispatchQueue)?

Да когда всё просто, нахуй! Кинул клоужер в async, получил результат в main — и свободен. Или нужно поставить барьер, чтоб синхронизировать доступ к ресурсу. Или использовать семафоры для тонкой настройки. GCD — это как отвёртка: быстрая, простая, для базовых задач.

А NSOperationQueue — это уже целый набор инструментов, который, кстати, часто внутри себя использует тот же GCD. Но даёт тебе такую обёртку и контроль, что просто волосы дыбом встают от удобства для сложных сценариев.

Короче, если задача простая — GCD и не парься. Если начинаешь чувствовать, что код превращается в спагетти из колбэков и семафоров — это верный знак, что пора пересаживаться на NSOperationQueue. Он хоть и выглядит посерьёзнее, но в итоге спасает от ебалы головной.