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)
}
}
系列文章鏈接