8-6.【OC】【NSOperation】NSBlockOperation 内部如何管理多个 block?

2 阅读3分钟

NSBlockOperationNSOperation 的一个非常实用的子类。它的独特之处在于,它不仅能执行一个任务,还能通过 addExecutionBlock: 动态地追加多个 Block

这种设计的核心在于:并发执行状态同步


1. 内部管理机制:Block 集合

在内部,NSBlockOperation 维护了一个**私有集合(数组)**来存储所有添加的 Block。

  • 初始任务:在初始化 blockOperationWithBlock: 时,第一个 Block 被存入集合。
  • 追加任务:随后每次调用 addExecutionBlock:,新的 Block 都会被加入这个集合。

2. 并发执行逻辑

这是最容易被误解的地方:虽然 NSBlockOperation 本身是一个“操作”对象,但它内部的多个 Block 并不一定是在同一个线程串行执行的。

  • 主从协作

    • NSBlockOperation 开始执行(被放入队列或手动调用 start)时,它会启动第一个 Block。
    • 对于集合中剩下的其他 Block,NSBlockOperation 会利用 GCD 的并发能力,将它们分发到后台线程并行执行。
  • 同步等待

    NSBlockOperationmain 函数(或 start 逻辑)会阻塞,直到所有追加的 Block 都执行完毕。


3. 状态机的收敛(IsFinished)

NSOperationQueue 依赖 isFinished 属性来判断一个任务是否结束。对于包含多个 Block 的 NSBlockOperation,其状态切换逻辑如下:

  1. 计数器管理:内部通常使用类似“信号量”或“计数器”的机制记录当前正在运行的 Block 数量。
  2. 原子性递减:每当一个 Block 执行结束,计数器减一。
  3. 触发完成:只有当计数器归零,即最后一个 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)来控制“一组任务”与“另一组任务”之间的先后顺序。