跳过正文

runloop理论剖析

·5 分钟
目录
tea (`image`)
tea (image)

什么是runloop
#

程序在启动的时候,一般按照代码顺序执行 ,执行完就结束。为了 维持一时刻运行等待用户输入事件的机制,程序不退出,出现了evenloop(消息/事件–观察- 唤醒-等待-),比如 Node.js 的事件处理,比如 Windows 程序的消息循环,再比如 OSX/iOS 里的 RunLoop。



为什么需要runloop
#

在无事件/消息的时候 程序进行休眠,节省计算资源。


ios 中的runloop
#

ios 中的runloop 是一个对象,OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。

  1. CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的,简称cfrunloop

  2. NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的

runloop 与线程一一对应,在线程上处理输入源,同时runloop 切换状态时候会发送通知给注册在runloop 中观察者。

运行循环的结构及其来源

运行循环的结构及其来源

runloop 与线程关系
#

Runloop是与线程相关的基础架构的一部分。Runloop是一个事件处理循环,用于安排工作和协调接收传入事件。Runloop的目的是在有工作要做时让你的线程保持忙碌,并在没有工作时让你的线程进入睡眠状态。

Ios app 不允许直接创建Runloop。它只提供了两个自动获取的函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 当然,Foudation 框架也有对应的API。

runloop对象
#

Core Foundation
#

CFRunLoopRef maiRunloop = CFRunLoopGetMain(); // 获得主线程对应的 runloop对象
CFRunLoopRef maiRunloop = CFRunLoopGetCurrent(); // 获得当前线程对应的runloop对象

Foundation
#

NSRunLoop *mainRunloop = [NSRunLoop mainRunLoop]; // 获得主线程对应的 runloop对象
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop]; // 获得当前线程对应的runloop对象

Run Loop Modes
#

运行模式是要监视的输入源和计时器的集合以及要通知的运行循环观察者的集合。每次你运行你的运行循环,你指定(显式或隐式)一个特定的“模式”运行。在运行循环的该阶段,只有与该模式关联的源被监控并允许传递它们的事件。 (类似地,只有与该模式关联的观察者会被通知运行循环的进度。)与其他模式关联的源会保留任何新事件,直到后续以适当的模式通过循环。

LoopMode
#

runloop 有相关的运行模式,运行时候进行多种切换,每个模式关联的 有【核心事件源/时间间隔,观察者】。再给线程加入相关的属于源以及观察者的时候需要给他们赋予模式。

共有5种模式,常用的模式有NSDefaultRunLoopMode,Event tracking 两种,默认模式是用于大多数操作的模式。大多数时候,你应该使用这个模式来启动你的运行循环并配置你的输入源。Cocoa 使用此模式来限制鼠标拖动循环和其他类型的用户界面跟踪循环期间的传入事件。

Input /Timer Source
#

数据源/定时器 ,事件源:目标监测源,使 RunLoop 在需要的时候唤醒线程,没事的时候 sleep

Input Source
#

输入源将事件异步传递到您的线程。事件的来源取决于输入源的类型,一般是两类之一。基于端口的输入源监控应用程序的 Mach 端口。自定义输入源监视自定义事件源。就你的运行循环而言,输入源是基于端口的还是自定义的都无关紧要。系统通常实现两种类型的输入源,您可以按原样使用。两个来源之间的唯一区别是它们是如何发出信号的。基于端口的源由内核自动发出信号,而自定义源必须从另一个线程手动发出信号。

  1. Cocoa Perform Selector Sources
  2. Port-Based Sources
  3. Custom Input Sources

Timer Sources
#

计时器源在未来的预设时间将事件同步传递到您的线程。定时器是线程通知自己做某事的一种方式。例如,一旦用户连续击键之间经过一定时间,搜索字段就可以使用计时器启动自动搜索。使用此延迟时间使用户有机会在开始搜索之前尽可能多地键入所需的搜索字符串。

虽然它会生成基于时间的通知,但计时器并不是一种实时机制。与输入源一样,计时器与运行循环的特定模式相关联。如果计时器不在运行循环当前正在监视的模式下,它不会触发,直到您以计时器支持的模式之一运行运行循环。类似地,如果在 run loop 正在执行处理程序例程的中间触发计时器,则计时器将等待直到下一次通过 run loop 调用其处理程序例程。如果运行循环根本没有运行,则计时器永远不会触发。

Runloop Observer
#

与在适当的异步或同步事件发生时触发的源相比,运行循环观察者在运行循环本身执行期间在特殊位置触发。您可以使用运行循环观察器来准备线程以处理给定事件或在线程进入睡眠之前准备线程。您可以将运行循环观察者与运行循环中的以下事件相关联

  • The entrance to the run loop.运行循环的入口。
  • When the run loop is about to process a timer.当运行循环即将处理计时器时。
  • When the run loop is about to process an input source.当运行循环即将处理输入源时。
  • When the run loop is about to go to sleep.当运行循环即将进入睡眠状态时。
  • When the run loop has woken up, but before it has processed the event that woke it up.当运行循环被唤醒,但在它处理唤醒它的事件之前。
  • The exit from the run loop.退出运行循环。

什么时候使用runloop?
#

适用于您希望与线程进行更多交互的情况下才使用runloop。

以下任何操作,则需要启动runloop:

  • Use ports or custom input sources to communicate with other threads.//使用端口或自定义输入源与其他线程进行通信。
  • Use timers on the thread.//在线程上使用计时器。
  • Use any of the performSelector… methods in a Cocoa application.//在 Cocoa 应用程序中使用任何 performSelector… 方法。
  • Keep the thread around to perform periodic tasks.//保留线程以执行定期任务。

参考
#