Wait for all Operations in queue to finish before performing task

前端 未结 6 1862
小鲜肉
小鲜肉 2021-01-02 07:29

I have an Operation subclass and Operation queue with maxConcurrentOperationCount = 1.

This performs my operations in a sequential order that i add them which is goo

相关标签:
6条回答
  • 2021-01-02 07:39

    You can use operation dependencies to initiate some operation upon the completion of a series of other operations:

    let queue = OperationQueue()
    
    let completionOperation = BlockOperation {
        // all done
    }
    
    for object in objects {
        let operation = ...
        completionOperation.addDependency(operation)
        queue.addOperation(operation)
    }
    
    OperationQueue.main.addOperation(completionOperation)  // or, if you don't need it on main queue, just `queue.addOperation(completionOperation)`
    

    Or, in iOS 13 and later, you can use barriers:

    let queue = OperationQueue()
    
    for object in objects {
        queue.addOperation(...)
    }
    
    queue.addBarrierBlock {
        DispatchQueue.main.async {
            // all done
        }
    }
    
    0 讨论(0)
  • 2021-01-02 07:39

    Set the maximum number of concurrent operations to 1

    operationQueue.maxConcurrentOperationCount = 1
    

    then each operation will be executed in order (as if each was dependent on the previous one) and your completion operation will execute at the end.

    0 讨论(0)
  • 2021-01-02 07:42

    A suitable solution is KVO

    First before the loop add the observer (assuming queue is the OperationQueue instance)

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

    Then implement

    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:

    In Swift 4 it's much easier

    Declare a property:

    var observation : NSKeyValueObservation?
    

    and create the 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
        }
    }
    
    0 讨论(0)
  • 2021-01-02 07:47

    I use the next 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 讨论(0)
  • 2021-01-02 07:52

    Code at the end of the queue refer to this link

    NSOperation and NSOperationQueue are great and useful Foundation framework tools for asynchronous tasks. One thing puzzled me though: How can I run code after all my queue operations finish? The simple answer is: use dependencies between operations in the queue (unique feature of NSOperation). It's just 5 lines of code solution.

    NSOperation dependency trick with Swift it is just easy to implement as this:

    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 讨论(0)
  • 2021-01-02 07:55

    My solution is similar to that of https://stackoverflow.com/a/42496559/452115, but I don't add the completionOperation in the main OperationQueue but into the queue itself. This works for me:

    var a = [Int](repeating: 0, count: 10)
    
    let queue = OperationQueue()
    
    let completionOperation = BlockOperation {
        print(a)
    }
    
    queue.maxConcurrentOperationCount = 2
    for i in 0...9 {
        let operation = BlockOperation {
            a[i] = 1
        }
        completionOperation.addDependency(operation)
        queue.addOperation(operation)
    }
    
    queue.addOperation(completionOperation)
    
    print("Done                                                                     
    0 讨论(0)
提交回复
热议问题