Runloop 探秘(一)

什么是 Runloop?

从字面意思看,Runloop 是一个运行循环,类似跑圈的概念。Runloop 是和线程紧密相关的一个基础组件,是多线程相关的幕后英雄。在平常开发中可能很少碰到,但作为一名力求上进的程序猿,实在有必要了解其相关概念和原理。

Runloop 的基本作用

  1. 保持程序的持续运行;
  2. 处理 APP 中的各种事件(比如触摸事件、定时器事件、Selector 事件等等)
  3. 节省 CPU 资源,提高程序性能,该做事时做事,该休息时休息;
  4. …………

在之前的博客中,我们讲到过 IOS 程序的启动原理,传送门:IOS UIApplication 和 delegate 以及程序启动原理,打开程序后会执行 main 函数,如果没有 Runloop,会怎么样呢?

这里写图片描述

在没有 Runloop 的情况下,第三行后程序就结束了。如果有了 Runloop,
这里写图片描述
由于main函数里面启动了个RunLoop,所以程序并不会马上退出,保持持续运行状态。

IOS 程序 main 函数中的 Runloop
这里写图片描述
UIApplicationMain 函数内部就启动了一个 RunLoop,所以 UIApplicationMain 函数一直没有返回,保持了程序的持续运行,这个默认启动的 RunLoop 是跟主线程相关联的。

Runloop 与线程

  • Runloop 和线程是绑定在一起的。每个线程(包括主线程)都有一个对应的 Runloop 对象。我们并不能自己创建 Runloop
    对象,但是可以获取到系统提供的 Runloop 对象;
  • 主线程的 Runloop 会在应用启动的时候完成启动,其他线程的 Runloop 默认并不会启动,需要我们手动启动;
  • RunLoop在第一次获取时创建,在线程结束时销毁。

RunLoop 相关类

Core Foundation 中关于 RunLoop 的 5 个类:

  1. CFRunLoopRef
  2. CFRunLoopModeRef
  3. CFRunLoopSourceRef
  4. CFRunLoopTimerRef
  5. CFRunLoopObserverRef

CFRunLoopModeRef
这里写图片描述

CFRunLoopModeRef 代表 RunLoop 的运行模式,Runloop Mode 实际上是 Source,Timer 和 Observer 的集合,不同的 Mode 把不同组的 Source,Timer 和 Observer 隔绝开来。Runloop 在某个时刻只能跑在一个 Mode 下,处理这一个 Mode 当中的 Source,Timer 和 Observer。

苹果文档中提到的 Mode 有五个,一一解释如下:

  • kCFRunLoopDefaultMode:App 的默认 Mode,通常主线程是在这个 Mode 下运行;
  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响;
  • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用;
  • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到;
  • kCFRunLoopCommonModes: 这是一个占位用的 Mode,不是一种真正的 Mode,其包含 kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。

CFRunLoopSourceRef

CFRunLoopSourceRef 是事件源(输入源),事件源的分类:

Source0:非基于 Port 的,用于用户主动触发的事件;
Source1:基于 Port 的,通过内核和其它线程相互发送消息。

CFRunLoopTimerRef

CFRunLoopTimerRef 是基于时间的触发器,基本上说的就是 NSTimer,它会受到runloop 的 mode 的影响,而 GCD 的定时器不受 Runloop 的 mode 的影响。关于定时器,笔者会在后续博文作总结。

CFRunLoopObserverRef

CFRunLoopObserverRef 是观察者,能够监听 RunLoop 的状态改变。

可以监听的时间点如下所示:
这里写图片描述