iOS 面试之 Runloop 我告诉你

49 阅读2分钟
image.png

1. Runloop 的使用场景

  1. 监测卡顿(Matrix):创建两个观察者runLoopObserver,添加到主线程的 common 模式,标记 Runloop 的开始和结束两种状态;然后创建一个子线程定期检查主线程的 Runloop 状态,如果主线程 runloop 停留在「进入睡眠前」或者「唤醒后」的状态超过 x 秒,就判断为出现卡顿,此时要 dump 出堆栈信息,上报服务端

  2. 触摸事件响应:用户触摸屏幕产生触摸事件,操作系统将事件放到队列中,主线程的 Runloop 不断检查事件队列中是否有待处理的事件(如果有就传给 UIApplication 处理),官方文档就是这么说的

  3. 手势识别

  4. 刷新页面:如果页面 View 发生改变,调用 setNeedsDisplay or setNeedsDisplayInRect: 函数来使这个 View 失效,系统知道后,会在当前 Runloop 结束后,对这个 View 进行重新绘制 (页面刷新相关的官方文档

  5. performSelector

  6. 定时器 NSTimer:timer 注册后,Runloop 会在对应时间点设置好事件,时间点到了后再去执行对应注册的函数

  7. GCD 中的 dispatch_async:使用时,libDispatch 向主线程的 Runloop 发送消息,Runloop 拿到对应的 block,在 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ 执行 9.** AFNetworking2.0 的线程保活**:子线程获取到 Runloop,给这个 Runloop 添加了一个 Port,这个子线程就能一直保留着,不会结束。

  8. AsyncDisplayKit

2. Runloop 跟线程的关系

  1. 每个线程有且只有一个与之对应的 Runloop。
  2. 主线程的 Runloop 系统会自动开启,自己创建子线程,获取到 Runloop 后需要自己手动去开启 Runloop
  3. Runloop 不允许直接创建,而是通过函数 CFRunLoopGetCurrent (在子线程里调用) 和 CFRunLoopGetMain (获取主线程的 Runloop)。Runloop 会在子线程结束的时候销毁。

参考资料

RunLoop - 同是天涯打工人: 写得蛮生动的