RunLoop

4 阅读2分钟

RunLoop作用:

  • 线程保活,保证线程不被杀死。
  • 在于管理事件/消息,让线程在没有处理消息时进行休眠以避免资源占用,在有消息到来时立即唤醒。

RunLoop是什么:

RunLoop实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 “接受消息->等待->处理” 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。

RunLoop和线程的关系:

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

RunLoop的Mode:

一个RunLoop包含多个Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。

RunLoop.png

CFRunLoopSourceRef 是事件产生的地方。

Source0(内部任务): 处理 App 内部的事务。负责处理那些不需要内核通知、可以由 App 自己控制时机的任务(如 UI 刷新请求、performSelector)。

Source1 (外部信号): 监听系统底层的端口(Mach Port)。负责接收外界硬件信号(触摸、传感器、网络回包)。它是 App 与外界沟通的唯一硬件级入口

CFRunLoopTimerRef 是基于时间的触发器。

其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。

CFRunLoopObserverRef 是观察者。

每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。

定时器

一个 Timer 一次只能加入到一个 RunLoop 中。使用的时候,通常就是加入到当前的 runLoop 的 default mode 中,而 ScrollView 在用户滑动时,主线程 RunLoop 会转到 UITrackingRunLoopMode 。而这个时候, Timer 就不会运行。

有如下两种解决方案:

  • 第一种: 设置RunLoop Mode,例如NSTimer,我们指定它运行于 NSRunLoopCommonModes ,这是一个Mode的集合。注册到这个 Mode 下后,无论当前 runLoop 运行哪个 mode ,事件都能得到执行。

  • 第二种: 另一种解决Timer的方法是,我们在另外一个线程执行和处理 Timer 事件,然后在主线程更新UI。

RunLoop循环: SourceO (port).png

参考文献:

juejin.cn/post/684490… blog.ibireme.com/2015/05/18/…