OperationQueue — это высокоуровневая очередь задач в iOS и macOS, построенная поверх Grand Central Dispatch. По данным Apple Developer Documentation, 2026, OperationQueue управляет экземплярами Operation — объектами, инкапсулирующими единицу работы. В отличие от DispatchQueue, OperationQueue поддерживает зависимости между операциями, приоритеты, KVO-наблюдение и отмену запущенных задач. OperationQueue автоматически управляет пулом потоков, распределяя операции по доступным ресурсам системы.
Главное
OperationQueue — это класс из фреймворка Foundation, управляющий выполнением объектов Operation. В отличие от DispatchQueue, OperationQueue не требует явного указания serial или concurrent режима — количество одновременно выполняющихся операций регулируется свойством maxConcurrentOperationCount. Значение 1 превращает очередь в sequential, любое другое — в concurrent.
Operation — абстрактный класс, представляющий единицу работы. Каждая операция имеет состояние: ready, executing, finished или cancelled. Состояния совместимы с KVO (Key-Value Observing), что позволяет реагировать на изменения — например, обновлять UI при завершении операции. Operation автоматически управляет флагами isExecuting и isFinished.
По данным Apple (2026), OperationQueue использует GCD под капотом, но добавляет функциональность, недоступную в DispatchQueue: зависимости, приоритеты и отмена операций. Если приложение выходит на фоновый режим, OperationQueue приостанавливает выполнение, а при возвращении — возобновляет. OperationQueue также автоматически учитывает количество ядер процессора и выбирает оптимальное количество потоков.
Каждая операция проходит через четыре состояния: pending (ожидание), ready (готова к запуску), executing (выполняется) и finished (завершена). Состояние cancelled может наступить на любом этапе до завершения. Переходы между состояниями отслеживаются через KVO — это основа для реактивного обновления UI. OperationQueue автоматически удаляет завершённые операции из очереди и уведомляет зависимые операции о том, что их prerequisite выполнен, запуская их выполнение.
Operation — абстрактный класс, требующий переопределения метода main() или start(). В методе main() размещается код задачи, а состояние isExecuting и isFinished управляются автоматически. Для асинхронных операций требуется переопределить start() и вручную управлять флагами состояния.
BlockOperation — конкретная реализация Operation, выполняющая один или несколько блоков кода. BlockOperation становится concurrent, если добавить в неё несколько блоков через addExecutionBlock(). Завершается операция только после выполнения всех добавленных блоков. BlockOperation — удобная альтернатива для простых задач без наследования.
| Характеристика | Operation | BlockOperation |
|---|---|---|
| Тип класса | Абстрактный | Конкретный |
| Наследование | Требуется | Не требуется |
| Асинхронность | Ручное управление KVO | Автоматическое |
| Блоков кода | Один в main() | Один или несколько |
| Применение | Сложные задачи с состоянием | Простые однократные задачи |
| Подходит для | Зависимости, отмена, прогресс | Быстрые блоки, completion |
| Память | Выше из-за KVO и состояния | Минимальная, легковесная |
Для создания кастомной операции наследуйтесь от Operation и переопределите main(). Внутри проверяйте флаг isCancelled перед дорогими операциями, чтобы обеспечить быструю отмену. Это критично для загрузки больших файлов или пакетной обработки данных. Выбор между Operation и BlockOperation зависит от сложности задачи: для простых однократных действий достаточно BlockOperation, для переиспользуемой логики с состоянием — наследование от Operation.
Зависимости — ключевое преимущество OperationQueue перед DispatchQueue. Метод addDependency(_:) определяет, что операция B выполняется только после завершения операции A. Зависимости образуют directed acyclic graph (DAG): при добавлении циклической зависимости очередь игнорирует её, и операции не запускаются.
Приоритет операции задаётся свойством queuePriority со значениями: .veryLow, .low, .normal, .high, .veryHigh. Приоритет влияет на порядок запуска среди ready-операций, но не отменяет зависимости. OperationQueue сначала учитывает зависимости, затем — приоритет среди доступных операций.
Типичный сценарий — загрузка данных профиля: сначала загружаем пользователя, затем на основе его id загружаем друзей и посты. Установка зависимости между загрузкой пользователя и загрузкой друзей гарантирует корректный порядок без вложенных completion handler.
Свойство maxConcurrentOperationCount ограничивает количество одновременно выполняемых операций. Значение 1 создаёт sequential очередь, значение по умолчанию (NSOperationQueueDefaultMaxConcurrentOperationCount) — системно-оптимальное, зависящее от текущей нагрузки устройства. Правильная настройка этого параметра предотвращает чрезмерное потребление ресурсов: для загрузки изображений достаточно 4-6 concurrent операций, для CPU-интенсивных задач — количество ядер процессора.
Выбор между OperationQueue и DispatchQueue зависит от сложности задачи. DispatchQueue — лёгковесный инструмент для простых async-вызовов. OperationQueue — более тяжёлое решение для сложных сценариев с множеством взаимосвязанных задач. Apple рекомендует начинать с DispatchQueue и переходить на OperationQueue только при необходимости зависимостей или отмены. Для большинства iOS-проектов комбинация обоих инструментов даёт оптимальный баланс производительности и гибкости.
По данным Ray Wenderlich (2025), в крупных iOS-проектах OperationQueue используется для загрузки контента с прогрессом и отменой, а DispatchQueue — для всех остальных async-операций. Соотношение примерно 20 к 80 в пользу DispatchQueue.
Рассмотрим три примера: простой BlockOperation, кастомная Operation с зависимостями и отменяемая операция для загрузки данных.
Самый простой случай — выполнить блок на OperationQueue и обработать результат через completionBlock. Каждая Operation имеет встроенное свойство completionBlock, вызываемое после завершения main().
let queue = OperationQueue()
let operation = BlockOperation()
operation.addExecutionBlock {
let data = NetworkService.fetchData()
OperationQueue.main.addOperation {
self.updateUI(data)
}
}
queue.addOperation(operation)
Зависимость гарантирует, что parseOperation запустится только после завершения downloadOperation. Это устраняет необходимость во вложенных callback.
let download = BlockOperation { self.downloadJSON() }
let parse = BlockOperation { self.parseJSON() }
parse.addDependency(download)
let queue = OperationQueue()
queue.addOperations([download, parse], waitUntilFinished: false)
Переопределите main() с периодической проверкой isCancelled. Это позволяет немедленно остановить операцию при отмене, не дожидаясь завершения дорогой операции.
class ImageLoadOperation: Operation {
override func main() {
guard !self.isCancelled else { return }
let image = self.downloadImage()
guard !self.isCancelled else { return }
OperationQueue.main.addOperation { self.display(image) }
}
}
Отмена операции устанавливает флаг isCancelled в true, но не останавливает уже запущенный метод main(). Код внутри main() должен самостоятельно проверять isCancelled и завершаться при необходимости. Это архитектурное решение Apple — позволяющее разработчику корректно освободить ресурсы при отмене.
KVO-наблюдение за свойствами isFinished и isExecuting позволяет реагировать на завершение операций без explicit callback. OperationQueue автоматически удаляет завершённые операции из очереди, но они сохраняются в памяти до тех пор, пока на них есть сильные ссылки. KVO — основа для интеграции OperationQueue с реактивными фреймворками типа RxSwift или Combine.
Подписка на isCancelled через KVO позволяет обновлять UI при отмене операции — например, показывать placeholder вместо отменённой загрузки. Свойство isCancelled является KVO-совместимым, что делает его удобным для реактивных pipeline.
Не создавайте операции в больших количествах — каждая Operation является отдельным объектом в памяти. Если задача короткая и не требует зависимостей, используйте DispatchQueue напрямую. OperationQueue оправдана для сложных сценариев с явными зависимостями, отменой и мониторингом прогресса.
Проверяйте isCancelled перед дорогими операциями внутри метода main(). В случаях загрузки файлов или обработки изображений проверка после каждого значимого шага обеспечивает быстрый отклик на отмену. Используйте if isCancelled { return } в начале main() и после каждой крупной операции.
Правильно управляйте completionBlock. Свойство completionBlock операции вызывается после завершения main(), даже если операция была отменена. Проверяйте isCancelled внутри completionBlock, чтобы не обновлять UI ошибочными данными. OperationQueue.main — потокобезопасная очередь для UI-операций, аналогичная DispatchQueue.main.
Избегайте циклических зависимостей — они приводят к тому, что ни одна из операций в цикле никогда не запустится. OperationQueue не детектирует циклы автоматически: если A зависит от B, а B зависит от A, обе навсегда останутся в состоянии ready. Планируйте граф зависимостей заранее.
Часто задаваемые вопросы
OperationQueue построена поверх GCD и добавляет зависимости, приоритеты, KVO и отмену операций. DispatchQueue — более лёгкий инструмент для простых async задач без этих возможностей.
Установите свойство maxConcurrentOperationCount в 1. Это превращает OperationQueue в sequential очередь с сохранением всех преимуществ — зависимостей, приоритетов и отмены.
Метод cancel() устанавливает флаг isCancelled, но не останавливает выполняющийся метод main(). Код операции должен сам проверять isCancelled и завершаться. Отмена работает только для pending и ready операций.
Операцию стоит наследовать, когда требуется управление состоянием, асинхронность или переиспользование логики. BlockOperation подходит для простых однократных задач без наследования.
Нет, если не вызван метод waitUntilFinished с параметром true на главном потоке. Операции по умолчанию выполняются на фоновых потоках, а результат возвращается через OperationQueue.main.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также