NSBlockOperation 是 NSOperation 的一个非常实用的子类。它的独特之处在于,它不仅能执行一个任务,还能通过 addExecutionBlock: 动态地追加多个 Block。
这种设计的核心在于:并发执行与状态同步。
1. 内部管理机制:Block 集合
在内部,NSBlockOperation 维护了一个**私有集合(数组)**来存储所有添加的 Block。
- 初始任务:在初始化
blockOperationWithBlock:时,第一个 Block 被存入集合。 - 追加任务:随后每次调用
addExecutionBlock:,新的 Block 都会被加入这个集合。
2. 并发执行逻辑
这是最容易被误解的地方:虽然 NSBlockOperation 本身是一个“操作”对象,但它内部的多个 Block 并不一定是在同一个线程串行执行的。
-
主从协作:
- 当
NSBlockOperation开始执行(被放入队列或手动调用start)时,它会启动第一个 Block。 - 对于集合中剩下的其他 Block,
NSBlockOperation会利用 GCD 的并发能力,将它们分发到后台线程并行执行。
- 当
-
同步等待:
NSBlockOperation的main函数(或start逻辑)会阻塞,直到所有追加的 Block 都执行完毕。
3. 状态机的收敛(IsFinished)
NSOperationQueue 依赖 isFinished 属性来判断一个任务是否结束。对于包含多个 Block 的 NSBlockOperation,其状态切换逻辑如下:
- 计数器管理:内部通常使用类似“信号量”或“计数器”的机制记录当前正在运行的 Block 数量。
- 原子性递减:每当一个 Block 执行结束,计数器减一。
- 触发完成:只有当计数器归零,即最后一个 Block 执行完,
NSBlockOperation才会将自己的isExecuting设为NO,并将isFinished设为YES。
4. 关键行为特性
A. 灵活性与并发性
如果你想同时下载 5 张图片,并在全部完成后执行某个操作,你可以只用一个 NSBlockOperation:
Swift
let op = BlockOperation()
for url in urls {
op.addExecutionBlock {
downloadImage(url) // 这些会并发执行
}
}
op.completionBlock = { print("全部下载完成") }
B. 注意:执行线程的不确定性
虽然第一个 Block 通常在启动该 Operation 的线程运行,但后续追加的 Block 运行在哪个线程由系统调度决定。不能假设它们都在主线程或都在同一个后台线程。
C. 线程安全
NSBlockOperation 是线程安全的。你可以在任务运行的过程中,从另一个线程调用 addExecutionBlock:,只要此时 Operation 还没结束,新加入的 Block 就会被执行。
总结
| 特性 | NSBlockOperation 的表现 |
|---|---|
| 存储方式 | 内部数组/集合。 |
| 执行方式 | 并发执行(利用 GCD)。 |
| 结束判定 | 所有 Block 执行完毕才算 isFinished。 |
| 取消响应 | 若在执行前取消,所有 Block 都不跑;若执行中取消,已启动的无法中断,未启动的可能跳过。 |
💡 深度启发
NSBlockOperation 实际上在内部实现了一个微型的“任务组(Dispatch Group)” 。它将多个 Block 聚合为一个单一的 NSOperation 接口,这使得我们可以用依赖关系(Dependency)来控制“一组任务”与“另一组任务”之间的先后顺序。