Operation支持以下三种添加任务的方式
- 传
Operation
- 传闭包
- 传
Operation
数组
Operation管理
OperationQueue
会根据QoS
优先级以及其它依赖来执行状态为isReady
的Operation
。每个Operation
只能被执行一次,所以如果一个Operation
实例被添加到了一个队列中,那么它就不可以再被添加到别的队列中了。所以重复执行的任务可以通过实例化多个Operation
子类来达到目的。
等待完成
如果看OperationQueue
的底层,你会发现一个方法叫waitUntilAllOperationsAreFinished
。这个方法的调用会阻塞当前线程,所以不要在主线程调用。如果真的需要,就重新启用一个串行队列来调用它。
优先级
OperationQueue
就像DispatchGroup
,任务会有不同的QoS
优先级。
默认的优先级是.background
,类似DispatchGroup
,队列的优先级可能会因为任务的优先级更高而被升级来匹配。
暂停
设置isSuspended
属性为true
将会在暂停队列后续任务的执行。当前正在处理的任务还会继续,后续的任务知道isSuspended
为false
才会继续执行
最大Operation数
默认情况下,DispatchQueue
的最大任务数和设备能支持的并发数一样。
设置maxConcurrentOperationCount
即可限制。当它为1
的时候,这个队列就相当于是一个串行队列。
underlyingQueue
在还没有添加operation
之前,把一个已经存在的DispatchQueue
队列设置为underlyingQueue
会使得OperationQueue
里的所有任务QoS
优先级与DispatchQueue
优先级相同。这样设置可以将一系列不同的优先级的任务根据DispatchQueue
来重新设定。
例子
下面的代码在UITableView上展示经过滤镜过滤的图片,原图片从本地读取,滤镜图片则是实时渲染。
如果在主线程直接渲染会引起卡顿,这里通过OperationQueue
实现异步渲染和加载
例子中建立的默认参数Operation队列是串行的
class FilterTableViewController: UITableViewController {
private let queue = OperationQueue()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "example", for: indexPath) as! PhotoCell
let image = UIImage(named: "\(indexPath.row).png")!
let op = FilterOperation(image: image)
op.completionBlock = { // Operation完成闭包
DispatchQueue.main.async
{
guard let cell = tableView.cellForRow(at: indexPath) as? PhotoCell else {
return
}
cell.update(image: op.outputImage)
}
}
queue.addOperation(op) // 不需要主动调start(),加入队列之后就会根据系统资源安排执行
return cell
}
}
final class FilterOperation: Operation {
private static let context = CIContext()
var outputImage: UIImage?
private let inputImage: UIImage
init(image: UIImage) {
inputImage = image
super.init()
}
override func main() {
guard let filter = Filter(image: inputImage, radius: 3), // CIFilter封装
let output = filter.outputImage else {
print("Failed to generate tilt shift image")
return
}
let fromRect = CGRect(origin: .zero, size: inputImage.size)
guard let cgImage = TiltShiftOperation.context.createCGImage(output, from: fromRect) else {
print("No image generated")
return
}
outputImage = UIImage(cgImage: cgImage)
}
}
系列文章链接