在执行任务之前等待队列中的所有操作完成
Wait for all Operations in queue to finish before performing task
我有一个 Operation 子类和 Operation 队列,maxConcurrentOperationCount = 1。
这会按我添加它们的顺序执行我的操作,这很好,但现在我需要等到所有操作完成后再 运行 另一个进程。
我试图使用通知组,但由于这是 运行 在 for 循环中,一旦操作被添加到队列中,通知组就会触发。我如何等待所有操作完成在 运行 另一个进程之前离开队列?
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
}
一个合适的解决方案是 KVO
首先在循环之前添加观察者(假设queue
是OperationQueue
实例)
queue.addObserver(self, forKeyPath:"operations", options:.new, context:nil)
然后实施
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)
}
}
编辑:
在 Swift 4 中更容易
声明一个 属性:
var observation : NSKeyValueObservation?
并创建观察者
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
}
}
因为 iOS13 和 macOS15 operationCount
已弃用。替换是观察 progress.completedUnitCount
.
另一种现代方式是使用 Combine
的 KVO 发布者
var cancellable: AnyCancellable?
cancellable = queue.publisher(for: \.progress.completedUnitCount)
.filter{[=14=] == queue.progress.totalUnitCount}
.sink() { _ in
print("queue finished")
self.cancellable = nil
}
您可以使用操作依赖项在完成一系列其他操作后启动一些操作:
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)`
或者,在 iOS 13 及更高版本中,您可以使用障碍:
let queue = OperationQueue()
for object in objects {
queue.addOperation(...)
}
queue.addBarrierBlock {
DispatchQueue.main.async {
// all done
}
}
我使用下一个解决方案:
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)
}
}
设置最大并发数为1
operationQueue.maxConcurrentOperationCount = 1
然后每个操作将按顺序执行(就好像每个操作都依赖于前一个操作一样)并且您的完成操作将在最后执行。
队列末尾的代码
参考this link
NSOperation 和 NSOperationQueue 是用于异步任务的优秀且有用的基础框架工具。但有一件事让我感到困惑:在我的所有队列操作完成后,我如何 运行 编码?简单的答案是:使用队列中操作之间的依赖关系(NSOperation 的独特功能)。仅需5行代码解决方案
NSOperation 依赖技巧
使用 Swift 很容易实现,如下所示:
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([=10=]) }
NSOperationQueue().addOperation(doneOperation)
}}
我的解决方案与 的解决方案类似,但我没有将 completionOperation
添加到主 OperationQueue 中,而是添加到队列本身中。这对我有用:
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 ")
我有一个 Operation 子类和 Operation 队列,maxConcurrentOperationCount = 1。
这会按我添加它们的顺序执行我的操作,这很好,但现在我需要等到所有操作完成后再 运行 另一个进程。
我试图使用通知组,但由于这是 运行 在 for 循环中,一旦操作被添加到队列中,通知组就会触发。我如何等待所有操作完成在 运行 另一个进程之前离开队列?
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
}
一个合适的解决方案是 KVO
首先在循环之前添加观察者(假设queue
是OperationQueue
实例)
queue.addObserver(self, forKeyPath:"operations", options:.new, context:nil)
然后实施
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)
}
}
编辑:
在 Swift 4 中更容易
声明一个 属性:
var observation : NSKeyValueObservation?
并创建观察者
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
}
}
因为 iOS13 和 macOS15 operationCount
已弃用。替换是观察 progress.completedUnitCount
.
另一种现代方式是使用 Combine
var cancellable: AnyCancellable?
cancellable = queue.publisher(for: \.progress.completedUnitCount)
.filter{[=14=] == queue.progress.totalUnitCount}
.sink() { _ in
print("queue finished")
self.cancellable = nil
}
您可以使用操作依赖项在完成一系列其他操作后启动一些操作:
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)`
或者,在 iOS 13 及更高版本中,您可以使用障碍:
let queue = OperationQueue()
for object in objects {
queue.addOperation(...)
}
queue.addBarrierBlock {
DispatchQueue.main.async {
// all done
}
}
我使用下一个解决方案:
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)
}
}
设置最大并发数为1
operationQueue.maxConcurrentOperationCount = 1
然后每个操作将按顺序执行(就好像每个操作都依赖于前一个操作一样)并且您的完成操作将在最后执行。
队列末尾的代码 参考this link
NSOperation 和 NSOperationQueue 是用于异步任务的优秀且有用的基础框架工具。但有一件事让我感到困惑:在我的所有队列操作完成后,我如何 运行 编码?简单的答案是:使用队列中操作之间的依赖关系(NSOperation 的独特功能)。仅需5行代码解决方案
NSOperation 依赖技巧 使用 Swift 很容易实现,如下所示:
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([=10=]) }
NSOperationQueue().addOperation(doneOperation)
}}
我的解决方案与 completionOperation
添加到主 OperationQueue 中,而是添加到队列本身中。这对我有用:
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 ")