Wait for all operations in the queue to complete before completing the task.

I have a subclass of Operation and Operation queue with maxConcurrentOperationCount = 1.

This performs my operations in sequential order to add them, which is good, but now I need to wait for all operations to complete before starting another process.

I tried to use a notification group, but since it runs in a for loop, as soon as operations have been added to the queue, the notification group starts. How do I wait for all operations to leave the queue before starting another process?

for (index, _) in self.packArray.enumerated() { myGroup.enter() let myArrayOperation = ArrayOperation(collection: self.outerCollectionView, id: self.packArray[index].id, count: index) myArrayOperation.name = self.packArray[index].id downloadQueue.addOperation(myArrayOperation) myGroup.leave() } myGroup.notify(queue: .main) { // do stuff here } 
+6
source share
5 answers

You can use operational dependencies to start some operation after completing a number of other operations:

 let operationQueue = OperationQueue() let completionOperation = BlockOperation { // do something } for object in objects { let operation = ... completionOperation.addDependency(operation) operationQueue.addOperation(operation) } OperationQueue.main.addOperation(completionOperation) 
+12
source

A suitable solution is KVO

Before the loop adds an observer (it is assumed that the queue is an instance of OperationQueue )

 queue.addObserver(self, forKeyPath:"operations", options:.new, context:nil) 

Then do

 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if object as? OperationQueue == queue && keyPath == "operations" { if queue.operations.isEmpty { // Do something here when your queue has completed self.queue.removeObserver(self, forKeyPath:"operations") } } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } 

Edit:

Swift 4 makes it a lot easier.

Declare a property:

 var observation : NSKeyValueObservation? 

and create an observer

 observation = queue.observe(\.operationCount, options: [.new]) { [unowned self] (queue, change) in if change.newValue! == 0 { // Do something here when your queue has completed self.observation = nil } } 
+2
source

I am using the following solution:

 private let queue = OperationQueue() private func addOperations(_ operations: [Operation], completionHandler: @escaping () -> ()) { DispatchQueue.global().async { [unowned self] in self.queue.addOperations(operations, waitUntilFinished: true) DispatchQueue.main.async(execute: completionHandler) } } 
0
source

Set the maximum number of simultaneous operations to 1

 operationQueue.maxConcurrentOperationCount = 1 

then each operation will be performed in order (as if each of them depended on the previous one), and your completion operation will be performed at the end.

0
source

The code at the end of the queue refers to this link.

NSOperation and NSOperationQueue are excellent and useful Foundation framework tools for asynchronous tasks. One thing puzzled me: How can I run the code after completing all operations with my queues? The simple answer is to use dependencies between operations in the queue (a unique NSOperation function). These are just 5 lines of code.

The NSOperation approach with Swift can simply be implemented as follows:

 extension Array where Element: NSOperation { /// Execute block after all operations from the array. func onFinish(block: () -> Void) { let doneOperation = NSBlockOperation(block: block) self.forEach { [unowned doneOperation] in doneOperation.addDependency($0) } NSOperationQueue().addOperation(doneOperation) }} 
0
source

Source: https://habr.com/ru/post/1264805/


All Articles