二、Operation
上篇文章我们看了GCD,这篇我们来讨论一下Operation。相较于GCD,Operation是一个class,所以我们对于operation里的任务有更多的掌控权,比如我们可以随时查看这些任务的状态、取消任务、operation之间建立依赖关系、operation的可持续再用等。operation一共有四个状态
- isReady - operation已初始化可以开始了
- isExecuting - operation.start()之后
- isCancelled - 开始之后call了operation.cancel()
- isFinished - 没call cancel()正常完成之后
这些都是只读Bool,你可以控制变化的状态就两个
- start(): isReady -> isExecuting
- cancel(): isExecuting -> isCancelled
1、BlockOperation
Operation是class,所以你可以写自定义的class来继承它,然后实现自己的并发逻辑。或者直接用swift提供给你的BlockOperation:
let blockOperation = BlockOperation() // 初始方法一
let blockOperation = BlockOperation { // 初始方法二
print("hello shit")
}
你可以在初始化的时候就传入一个closure或者不传,你之后都可以再通过addExecutionBlock添加多个closures给BlockOperation;这些closures都会在一个默认的global queue并发进行。
let blockOperation = BlockOperation()
let str = "I always come to Starbucks"
let arr = str.split(separator: " ")
for s in arr {
blockOperation.addExecutionBlock {
print(s)
sleep(3)
}
}
blockOperation.completionBlock = { // a
print("finish")
}
// 输出的结果:
to
come
I
Starbucks
always
---- 3秒后 ----
finish
a) 跟DispatchGroup类似,blockOperation在它包含的所有task结束后也有通知你的方法,就是completionBlock
2、OperationQueue
和DispatchGroup类似,我们可以把我们创建的多个operations都放到OperationQueue当中;好处有以下几点:
a) 可以并发执行所有operation
b) 可以暂停执行
c) 可以限制最大执行数量
d) 我们一般不要直接执行operation.start(),应该把operation放到operation queue里,这样它会自动开始
let queue = OperationQueue() // 默认的qos是 .backgroud
queue.addOperation(op)
queue.isSuspended = true // 暂停执行新加入的operations,之前的还是会执行
queue.maxConcurrentOperationCount = 3 // 限制并发执行数量
3、Operation Dependencies
如果我们选择使用Operation而不是GCD,最大原因可能就是Operation之间可以相互依赖。比如我们有两个任务要做:
- task1:下载图片
- task2:渲染图片
这两个任务都不知道需要多长时间,而且都不应该影响UI,所以我们肯定是需要放到backgroud thread来执行。task2显然是要等task1结束才行,这时候如果用gcd,需要写很难读的nested代码才行,比较麻烦;但用operation,就可以简单写成
class downloadOperation: Operation { ... }
class filterOperation: Operation { ... }
let op1 = downloadOperation()
let op2 = filterOperation()
op2.addDependency(op1)
而且op2可以很方便的从op1中获取下载后的图片
protocol ImageProvider {
var image: UIImage? { get }
}
extension downloadOperation: ImageProvider {
var image: UIImage? {
return downloadedImage
}
extension filterOperation: ImageProvider {
var image: UIImage? {
return filteredImage
}
let resultImage = filterOperation.dependencies
.compactMap { ($0 as? ImageProvider)?.image }
.first
}
4、cancel operation
你可以用operation.cancel()来取消一个operation的执行,那么这个operation的isCancelled变为true,如果这个operation还没start()的话,就不会再执行了;可是如果你要取消已经开始的operation,你就得自己写逻辑了!比如在刚才的downloadOperation中,你可以把下载的task给取消等
task = URLSession.shared.dataTask(with: url) { ... }
task?.cancel()
5、相互依赖的deadlock
假设op1 依赖 op2, op2依赖op3,op3依赖op1,哦豁~妥妥的deadlock,没法解决而且很难debug,你只有写代码的时候清醒一点
QUIZ
分享一下我看书学concurrency时的作业:通过这两篇讨论Concurrency的文章,我们来写一个小app,就只有一个tableView,在自己创建的plist里有5个图片的url,每个cell显示一张图片,要求是这张图片要先下载,再通过CIFilter(不太会用的同学请自行上网查一下)给图片加一个滤镜(哪种滤镜up to you),然后再显示!
我也正在写,写完会放到Github上