【OC源码】Runloop | 三. runloop 的执行流程

这个流程大多数参考文章都已经比较旧, 参考价值不大, 所以直接对照源码解析

我对 runloop 源码的解析:
数据结构源码解析
CFRunloopRun 源码解析
一些其他函数解析

所使用的源码版本:
CF-1151.16.tar

本文主要是对 Runloop 执行过程的解读, 对源码的主要分析过程在此: CFRunloopRun 源码解析

函数调用流程

CFRunLoopRun

最外层的函数, 主要是提供给外层的封装
主要是在维护一个循环, 然后调用当前线程, 以默认模式(kCFRunLoopDefaultMode)执行函数 CFRunLoopRunSpecific

CFRunLoopRunSpecific

这是个通用 runloop 执行函数, 依赖传入的线程,传入的模式, 超时等
其主要职责是负责Mode 过滤及切换
对于空 Mode, 在这一步被过滤, 不会真正执行

 if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {  
    Boolean did = false;  
    return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;  
}  

由于有效 mode, 缓存起上一次的 Mode, 发送通知, 并执行 __CFRunLoopRun

✨₁ 此处调用了 Observer (之后会做归纳)

if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);  
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);  
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);  

__CFRunLoopRun

源码解析见 CFRunloopRun 源码解析
其流程如下:

图中 “问号” 部分为看源码不确定的内容, 其中 source1 和休眠都涉及到 mach( 其他: Mach 是什么 )
对应的步骤中,会调用数据结构中提到的函数 二. runloop 的数据结构
解析写在 其他: 一些其他函数解析(CFRunLoopDoObservers__CFRunLoopDoBlocks__CFRunLoopDoSources0)

  • 调用 Observer: __CFRunLoopDoObservers
  • 执行 Sources0: __CFRunLoopDoSources0
  • 处理 Timer: __CFRunLoopDoTimers
  • 处理 Blocks: __CFRunLoopDoBlocks

流程:

  1. loop 状态判断: 查是否已经结束, 如果结束,直接走 return
  2. 设置超时: 根据入参, 设置对应的超时
  3. 初始化循环条件 & 进入循环
  4. 调用 Observer: (✨₃)kCFRunLoopBeforeTimers (其实离执行还有段时间,Timers 在 source 0 之后)
  5. 调用 Observer: (✨₄)kCFRunLoopBeforeSources
  6. 处理 Blocks: 我们 dispatch_asyn, dispatch_sync 的内容, 就是到了 blocks 中
  7. 执行 Sources 0: 执行 source 0
  8. 处理 Blocks: 我推测是执行 source0 的过程中可能导致新的 blocks 被加入, 所以又执行了一次(后面还有几次类似的操作)
  9. 判断 Source 1: 这两块代码被一些宏控制, 我没找到宏定义. 但是如果不是这就没地方执行 source 1, 内部是 match 的调用
  10. 处理 Sources 1: 这块根据判断, 如果有 source1, 就不会进入休眠, 而是直接跳到16
  11. 调用 Observer: (✨₅)kCFRunLoopBeforeWaiting
  12. 准备休眠: 代码中设置了一些休眠状态
  13. 开始休眠: 源码其实没找到正在的休眠逻辑, 猜测是__CFRunLoopServiceMachPort内部带了
  14. 休眠结束:
  15. 调用 Observer: (✨₆)kCFRunLoopAfterWaiting
  16. 处理 Timer: 一个很大的 if-else, 根据宏判断最后会执行到 __CFRunLoopDoTimers
  17. 处理 Blocks: 同上blocks, 应该是因为 Timer 中可能包含 block, 所以又执行一次
  18. 决定执行结果, 重新循环,或者结束

Runloop 休眠

❓这块我没看明白源码,调用的是__CFRunLoopServiceMachPort
根据其他参考文章,其内部的 mach_msg 是切换到 “内核态”:

  • 没有消息时, 保持休眠
  • 有消息时, 唤醒线程

Observer 所有被触发的时机

(✨ 表示文章的定位位置)

  1. kCFRunLoopEntry: (✨₁) 执行真正的 loop 前(__CFRunLoopRun)
  2. kCFRunLoopBeforeTimers: (✨₃) 开始 Timer 前(其实 source 更早)
  3. kCFRunLoopBeforeSources:(✨₄) 开始 Source 0 前
  4. kCFRunLoopBeforeWaiting:(✨₅) 开始 休眠 前
  5. kCFRunLoopAfterWaiting: (✨₆) 结束休眠
  6. kCFRunLoopExit : (✨₂) loop 执行完毕

#猿人/猿艺/iOS/基石/runloop/正文