Run Loops官方文档翻译(一)

来源:互联网 发布:mac os最新版本 编辑:程序博客网 时间:2024/06/14 07:00

Run Loops

Run loops是与线程相关的基础框架的一部分。一个run loop是一个循环,在这个循环中你可以分配任务,并协调安排接收到的各种事件。run loop存在的目的是使线程在有任务的时候处于工作状态,并且当没有任务的时候使线程处于休眠状态.
Run loop的管理不完全是自动的,你仍然需要设计跟线程有关的代码,以便在适当的时候启动运行循环,并响应传入的事件。
Cocoa和Core Foundation都提供一些与run loop相关的对象来帮助你配置和管理线程的run loop。你的应用程序不需要显式的创建这些对象;每一个线程,包括主线程都有一个相关联的run loop对象。子线程的run loop对象需要手动开启,而系统的app framework会在app启动时候 自动设置并开启主线程的run loop。

Run Loop剖析

run loop就像它的名字含义一样。它是一个线程可以进入其中并用来运行事件处理程序以响应传入事件的循环。我们可以通过代码控制循环,换句话说,我们可以在代码中用for循环或者while循环控制run loop。在循环内部,我们可以用run loop对象去运行事件处理代码,这些代码可以接收外部事件,并且调用已经绑定到run loop对象的回调函数。
run loop接收来自两种不同类型的源的事件。输入源提供异步事件,通常是来自另一个线程或不同应用程序的消息。定时器源提供同步事件,发生在预定时间或重复间隔。这两种类型的源都使用应用程序特别指定的处理程序来处理到达的事件。
下图显示了run loop和各种源的结构。输入源将异步事件传递给相应的处理程序,并导致runUntilDate:方法(在与线程关联的NSRunLoop对象上调用)退出。定时器源将事件传递到其处理程序例程,但不会导致run loop退出。
图片.png
除了处理输入源之外,run loop同时也生成了有关run loop各种状态的通知。注册run loop的observers可以收到这些通知,并且可以在线程上做一些其他操作。通过Core Foundation框架可以在线程上添加observer

Run Loop Modes

Run Loop Modes是要监视的输入源和定时器的集合,以及要通知的运行循环observer的集合。每次运行run loop时,都需要(显式或隐式)指定特定的“模式”来运行。在这次run loop过程中,只有与该模式相关的源会被监听,并允许其发送事件。 (类似地,只有与该模式相关的observer才会被通知run loop的状态。)与其他模式相关的源会保留发送给它们事件,直到随后以适当的模式运行。
在我们的代码中,我们可以通过名字来区分模式。 Cocoa和Core Foundation都定义了一个默认模式和若干个常用模式,以及用于在代码中标志这些模式的字符串。我们可以通过自定义一个字符串作为名字来定义模式。虽然这个字符串可以是任意的,但是模式的内容却不是任意的。为了确保创建的模式(models)可用,必须向其中添加至少一个或多个输入源(input sources),定时器(timers),或者run loop观察者(run loop observer)。
我们可以使用模式在特定的运行循环模式下过滤掉不需要的源事件。在大多数情况下,我们是在系统已经定义好的”default”模式下。然而,模态面板可能以”模态”模式运行(注:这段不是很懂,不了解模态面板和模态模式)。在这种模式下,只有与该模式相关的源才会将事件传递给线程。对于其他次级线程,我们可以通过自定义models用来阻值低优先级的源在限时操作中传递事件。

注意:模式基于事件的来源而不是事件的类型进行区分。 例如,你不会使用模式来匹配手指触摸事件或键盘输入事件。 你可以使用模式来监听一组不同的端口,计时器临时的暂停,或者更改当前正在监视的源(source)以及run loop observer。

输入源(input source)

输入源异步发送事件到线程中。输入源的类型决定了事件的类型,通常有两种事件。Port-based输入源监听应用的Mach端口。自定义输入源监听自定义事件。就run loop而言,输入源是基于Mach端口或者其他端口并不重要。
当创建一个输入源后,可以将它添加run loop的一个或多个model中。在任何时刻,models决定了哪些输入源处于被监听状态。大多数时间,run loop处于default model下,但是可以切换到custom model下。如果一个输入源没有被添加到当前正在运行的模式下,它的所有事件都会被阻塞,直到run loop切换到该模式下。
下面几部分介绍了其中一些输入源(input source)

Port-Based Sources

Cocoa框架和Core Foundation框架内部都提供了与port相关的对象和方法,可以用来创建输入源。在Cocoa框架中,不用直接创建输入源,只需要创建一个port对象,并使用NSPort提供的方法将这个对象加入到run loop即可。port对象会为你管理输入源的创建和配置。
在Core Foundation中,你必须手动创建port和它的输入源。在这两种情况下,你可以使用与port类型(CFMachPortRef, CFMessagePortRef, or CFSocketRef)相关的函数去创建合适的对象。

Custom Input Sources

为了创建一个自定义输入源,你必须使用Core Foundation中与CFRunLoopSourceRef相关的函数。你使用几个回调函数来配置一个输入源。Core Foundation框架会在不同时刻调用这些函数以配置源、处理任何输入事件,并且当source被移除run loop时候会被自动释放掉。
除了需要定义回调函数之外,还必须定义事件传递机制。这部分的源代码在单独的线程上运行,负责为输入源提供数据,并在数据准备好处理时用信号通知它。 事件传递机制取决于你,但不必过于复杂。

Cocoa Perform Selector Sources

除了基于端口的源之外,Cocoa还定义了一个自定义输入源,允许你在任何线程上执行选择器。 像基于端口的源一样,执行选择器请求在目标线程上被序列化,减轻了在一个线程上运行多个方法时可能发生的许多同步问题。 与基于端口的源不同,执行选择器源在执行选择器之后将自身从运行循环中移除。

注意:在OS X v10.5之前,执行选择器源主要用于向主线程发送消息,但在OS X v10.5及更高版本和iOS中,可以使用它们向任何线程发送消息。

在另一个线程上执行选择器时,目标线程必须有一个活动的运行循环。 对于你创建的线程,这意味着等待直到你的代码明确地启动运行循环。 因为主线程启动自己的运行循环,所以只要应用程序调用应用程序委托的applicationDidFinishLaunching:方法,就可以开始在该线程上发出调用。 运行循环每次通过循环处理所有排队的执行选择器调用,而不是在每个循环迭代中处理一个。

timers

定时器源在未来的预设时间将事件同步传递给你的线程。定时器是线程通知自己做某事的一种方式。例如,search field可以使用定时器来在用户的连续输入之间经过一定的时间后启动自动搜索。这个延迟时间的使用使用户有机会在开始搜索之前尽可能多地输入想要的搜索字符串
虽然它会生成基于时间的通知,但计时器不是实时机制。像输入源一样,定时器与run loop的特定模式相关联。如果一个定时器不在当前被运行循环监视的模式下,它不会被触发,直到处于定时器支持的一种模式下运行时才会被触发。同样的,如果一个定时器在run loop处于执行处理程序的过程中触发,定时器会等待下一次通过run loop来调用它的处理程序。如果run loop根本没有运行,定时器不会启动。
你可以将定时器配置为仅生成一次或重复生成事件,重复的计时器会根据预订的触发时间而不是实际的触发时间自动重新安排时间。例如,如果计时器在某个时间以及之后的每5秒钟触发一次,则即使实际的触发被延迟,计划的触发时间将总是以原来的5秒的时间间隔下降。如果触发时间延迟过多,以至于错过了一个或多个预定的触发时间,则计时器仅在错过的时间段内触发一次。在错过的时间触发之后,定时器重新计划下一个预定的触发时间。

Run Loop Observers

与源不同,run loop observers是当某些同步事件或者异步事件发生的时候被触发。run loop observers在run loop中的一个特定状态被触发。你可以通过run loop observer去让线程在run loop的开始状态或者休眠状态执行特定的动作。你可以将run loop observer与run loop中的以下几种状态关联:
1.即将进入run loop
2.run loop即将处理timer
3.run loop即将处理输入源
4.run loop即将进入休眠状态
5.run loop已经被唤醒,但还没处理唤醒它的事件
6.run loop退出循环
你可以通过Core Foundation去添加run loop observer。如果要创建run loop observer,首先创建CFRunLoopObserverRef的实例。这个实例会将你自定义的回调函数与它关心的run loop状态关联。
同timer类似,run loop observer可以被一次或重复使用。一个一次性的run loop observer会在它触发后将自己从run loop移除。但是重复使用的observer不会这样。你可以在创建observer的时候指定它是否重复。

run loop事件队列

每次运行run loop时候,线程的run loop会处理即将发生的事件,并且会为所有observer生成通知。它会以特定的顺序执行这些操作,顺序如下:
1.通知观察者run loop已经进入循环
2.通知观察者任何准备好的timer即将被触发
3.通知观察者任何port端口之外的输入源将要触发
4.触发任何非基于端口(source0)的输入源事件
5.如果有基于端口的输入源事件(source1),立刻处理这些事件,并跳转到第9步
6.通知观察者,线程将要休眠
7.将线程置于休眠状态,直到下列事件发生:
* port端口事件到达
* timer 触发
* 到达run loop超时时间
* run loop被显示唤醒

8.通知观察者线程刚被唤醒
9.处理唤醒run loop的事件
* 如果是用户定义的timer触发时间到了,处理timer事件,并重新开启run loop。跳转到第二步
* 如果是输入源事件,处理这个事件
* 如果run loop是被显式调用,并且还未到run loop的超时时间,重新开启runloop 跳转到第二步

10.通知observer,run loop已经退出循环。
图片.png
由于timer和输入源的观察者是先收到通知,在触发事件。所以通知的时间和实际事件触发的时间之间可能存在差距。如果这些事件之间的时间很关键,则可以通过使用休眠和从休眠中醒来的通知来获得实际事件发生之前的时间。

原创粉丝点击