Runloop

191623314639_.pic.jpg

Runloop介绍

Runloop(运行循环)是与线程关联的基础结构的一部分。一个运行循环是一个事件处理循环,用它来安排工作,并协调接收传入的事件。运行循环的目的是在有工作要做时让线程忙,而在没有工作时让线程进入睡眠状态。

运行循环管理不是完全自动的。必须通过设计线程在适当的时候启动运行循环并响应传入的事件。Cocoa和Core Foundation都提供了运行循环对象,以帮助您配置和管理线程的运行循环。您的应用程序不需要显式创建这些对象。每个线程(包括应用程序的主线程)都有一个关联的运行循环对象。但是,只有辅助线程需要显式地运行其运行循环。在应用程序启动过程中,应用程序框架会自动在主线程上设置并运行运行循环。

一、Runloop解剖

运行循环和其字面意思一致,使线程进入的一个循环,用于响应传入事件并执行事件处理。

image.png

Runloop官方介绍

除了处理响应事件之外,运行循环还生成有关运行循环行为的通知。注册的运行循环观察者可以接收这些通知,并使用它们在线程上进行其他处理。您可以使用Core Foundation在线程上安装运行循环观察器。

二、CFRunloop

1、CFRunloop介绍

A CFRunLoop object monitors sources of input to a task and dispatches control when they become ready for processing. Examples of input sources might include user input devices, network connections, periodic or time-delayed events, and asynchronous callbacks.

CFRunLoop对象监视任务的输入源,并进行调度控制处理。这里输入源包括输入设备,网络连接,周期性或延时事件以及异步回调。

2、运行模式介绍

RunLoop可以监视三种类型的输入源对象:CFRunLoopSource、CFRunLoopTimer、CFRunLoopObserver,为了能实现监视输入源,必须先将这些对输入源对象加到Runloop内,同样也可以取消这些源的监听。

添加到运行循环中的每个源对象必须与一个或多个运行循环模式相关联。模式决定在给定的时间段中运行循环处理什么事件。每次运行循环执行时,它都以特定的模式执行。在该模式下,运行循环只处理与该模式关联的源对象相关的事件。将大多数源分配到默认运行循环模式defaultmode
,该模式用于在应用程序(或线程)空闲时处理事件。系统也定义了其他运行模式,在这些模式中执行运行循环,监听控制不同的源对象。还可以定义自己的自定义模式来限制事件的处理。

截屏2021-05-28 下午4.49.16.png

了解到运行模式后,介绍一个伪模式commonmodes(通用模式),通用模式是一组运行循环模式,可以为其定义一组由这些模式共享的源对象。例如,无需将源注册到每个特定的运行循环模式,只需将其注册到运行循环的通用模式一次,它将在每个运行循环模式下以公共模式集自动注册。同样,当将模式添加到通用模式时,已注册到该通模式的任何源,都将添加到通用模式中。

注意: 模式是根据事件的来源而不是事件的类型来区分的。

3、输入源

输入源将事件异步传递到线程。事件的来源取决于输入来源的类型,主要两种类型:基于接口输入、自定义输入。

注意:如果运行循环没有任何要监视的源,它会立即退出
接口输入的源:

Cocoa和Core Foundation提供了内置支持,用于使用与端口相关的对象和功能来创建基于端口的输入源。不需要直接创建输入源,只需创建一个端口对象,然后使用的方法NSPort将该端口添加到运行循环中即可。端口对象为您处理所需输入源的创建和配置。

自定义输入:

若要创建自定义输入源,必须CFRunLoopSourceRef在Core Foundation中使用与不透明类型关联的功能。可以使用多个回调函数配置自定义输入源。

  • 选择器源
    选择器源可让你在任何线程上执行选择器。像基于端口的源一样,执行选择器请求在目标线程上,从而减轻了在一个线程上运行多种方法时可能发生的许多同步问题。与基于端口的源不同,执行选择器源在执行选择器后将其自身从运行循环中删除。


    截屏2021-05-28 下午5.08.51.png
注意: 添加选择器源时,处理消息的对象会延迟释放,autorelease处理在后
  • 计时器源
    尽管计时器生成基于时间的通知,但它不是实时机制。像输入源一样,计时器与运行循环的特定模式相关联。如果计时器不在运行循环当前正在监视的模式下,则在您以计时器支持的一种模式运行运行循环之前,它不会触发。同样,如果运行循环在执行处理程序例程的中间触发计时器,则计时器将等到下一次通过运行循环调用其处理例程。如果运行循环根本没有运行,则计时器永远不会触发。

4、运行循环观察者

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

  • 运行循环的入口。
  • 当运行循环将要处理计时器时。
  • 当运行循环将要处理输入源时。
  • 当运行循环即将进入睡眠状态时。
  • 当运行循环醒来但在处理该事件之前将其唤醒。
  • 运行循环的退出。
/* Run Loop Observer Activities */
public struct CFRunLoopActivity : OptionSet {

    public init(rawValue: CFOptionFlags)

    
    public static var entry: CFRunLoopActivity { get }

    public static var beforeTimers: CFRunLoopActivity { get }

    public static var beforeSources: CFRunLoopActivity { get }

    public static var beforeWaiting: CFRunLoopActivity { get }

    public static var afterWaiting: CFRunLoopActivity { get }

    public static var exit: CFRunLoopActivity { get }

    public static var allActivities: CFRunLoopActivity { get }
}

5、事件的运行循环序列

每次运行它时,线程的运行循环都会处理未决事件,并为所有附加的观察者生成通知。它执行此操作的顺序非常具体,如下所示:

1.通知观察者已进入运行循环。
2.通知观察者任何准备就绪的计时器即将触发。
3.通知观察者任何不基于端口的输入源都将被触发。
4.触发所有准备触发的非基于端口的输入源。
5.如果基于端口的输入源已准备好并等待启动,请立即处理事件。转到步骤9。
6.通知观察者线程即将进入睡眠状态。
7.使线程进入睡眠状态,直到发生以下事件之一:

  • 事件到达基于端口的输入源。
  • 计时器触发。
  • 为运行循环设置的超时值到期。
  • 运行循环被明确唤醒。

8.通知观察者线程刚刚醒来。
9.处理未决事件。

  • 如果触发了用户定义的计时器,请处理计时器事件并重新启动循环。转到步骤2。
  • 如果触发了输入源,则传递事件。
  • 如果运行循环被明确唤醒,但尚未超时,请重新启动循环。转到步骤2。

10.通知观察者运行循环已退出。

你可能感兴趣的:(Runloop)