RunLoop相关知识点

NSRunLoop类声明的编程接口对象管理输入源。NSRunLoop对象处理鼠标和键盘等输入来源来自窗口系统的事件,NSPort对象,NSConnection对象。还一个NSRunLoop对象流程NSTimer事件编程

- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;xcode

+ (NSRunLoop *)currentRunLoop;异步

+ (NSRunLoop *)mainRunLoop NS_AVAILABLE(10_5, 2_0);函数

消息处理模式,对消息处理过程进行了更好的抽象和封装oop

 

 

 

1.输入事件来源:输入源(input source)和定时源(timer source).使用程序的某一特定的处理例程来处理到达的事件。线程

当你建立输入源,你须要将其分配给runloop中的一个或多个模式。模式只会在特定事件影响监听的源。大多数状况下,runloop运行在默认模式下,可是你也可使其运行在自定义模式下。若某一源在当前模式下不被监听,那么任何其生成的消息只在runloop运行在其关联的模式下才会被传递。code

传递异步事件,一般消息来自于其余线程或程序。输入源传递异步消息给相应的处理例程,并调用runUntilDate:方法来退出(在线程里面相关的NSRunLoop对象调用)orm

1.1 基于端口的输入源由内核自动发送对象

cocoa和core fundation内置支持使用端口相关的对象和函数来建立的基于端口的源。就是说,在cocoa里,历来不须要直接建立输入源。只要简单的建立端口对象,并使用NSPort的方法把该端口添加到runloop。端口队形会本身处理建立和配置输入源。接口

在core foundation中,你必须人工建立端口和它的runloop源。可使用端口相关的函数(CFMachPortRef,CFMessagePortRef,CFSocketRef)

来建立合适的对象。

如何建立一个基于端口的输入源, 将其添加到runloop并启动:

1.2自定义输入源须要人工从其余线程发送

自定义的输入源须要人工从其余线程发送。

为了建立自定义输入源,必须使用Core Foundation里面的CFRunLoopSourceRef类型相关的函数来建立。你可使用回调函数来配置自定义输入源。Core Fundation会在配置源的不一样地方调用回调函数,处理输入事件,在源从run loop移除的时候清理它。

除了定义在事件到达时自定义输入源的行为,你也必须定义消息传递机制。源的这部分运行在单独的线程里面,并负责在数据等待处理的时候传递数据给源并通知它处理数据。消息传递机制的定义取决于你,但最好不要过于复杂。建立并启动自定义输入源的示例以下

1.3 cocoa上的Selector源

除了基于端口的源,cocoa定义了自定义输入源,容许你在任何线程执行selector方法。和基于端口的源同样,执行selector请求会在目标线程上序列化,减缓许多在线程上容许方法容易引发的同步问题。不像基于端口的源,一个selector执行完后会自动从runloop里面移除。

当在其余线程上面执行selector时,目标线程需有一个活动的runloop。对于你建立的线程,这意味着线程在你显示的启动runloop以前是不会执行selector方法的,而是一直处于休眠状态。

1.4定时源

定时源在预设的时间点同步方式传递消息,这些消息都会发生在特定时间或者重复的时间间隔。定时源则直接传递消息给处理例程,不会当即退出run loop。

须要注意的是,尽管定时器能够产生基于时间的通知,但它并非实时机制。和输入源同样,定时器也和你的run loop的特定模式相关。若是定时器所在的模式当前未被run loop监视,那么定时器将不会开始直到run loop运行在相应的模式下。相似的,若是定时器在run loop处理某一事件期间开始,定时器会一直等待直到下次run loop开始相应的处理程序。若是run loop再也不运行,那定时器也将永远不启动。

建立定时器源有两种方法,

二。runloop观察者

2.1 源是在合适的同步或异步事件发生时触发,而run loop观察者则是在run loop自己运行的特定时候触发。你可使用run loop观察者来为处理某一特定事件或是进入休眠的线程作准备。你能够将run loop观察者和如下事件关联:

1>.  Runloop入口

2>.  Runloop什么时候处理一个定时器

3>.  Runloop什么时候处理一个输入源

4>.  Runloop什么时候进入睡眠状态

5>.  Runloop什么时候被唤醒,但在唤醒以前要处理的事件

6>.  Runloop终止

2.2 和定时器相似,在建立的时候你能够指定run loop观察者能够只用一次或循环使用。若只用一次,那么在它启动后,会把它本身从run loop里面移除,而循环的观察者则不会。定义观察者并把它添加到run loop,只能使用Core Fundation。下面的例子演示了如何建立run loop的观察者

三。runloop的事件队列

每次运行run loop,你线程的run loop对会自动处理以前未处理的消息,并通知相关的观察者。具体的顺序以下:

  1. 通知观察者run loop已经启动
  2. 通知观察者任何即将要开始的定时器
  3. 通知观察者任何即将启动的非基于端口的源
  4. 启动任何准备好的非基于端口的源
  5. 若是基于端口的源准备好并处于等待状态,当即启动;并进入步骤9。
  6. 通知观察者线程进入休眠
  7. 将线程置于休眠直到任一下面的事件发生:
    • 某一事件到达基于端口的源
    • 定时器启动
    • Run loop设置的时间已经超时
    • run loop被显式唤醒
  8. 通知观察者线程将被唤醒。
  9. 处理未处理的事件
    • 若是用户定义的定时器启动,处理定时器事件并重启run loop。进入步骤2
    • 若是输入源启动,传递相应的消息
    • 若是run loop被显式唤醒并且时间还没超时,重启run loop。进入步骤2
  10. 通知观察者run loop结束。
  11. 由于定时器和输入源的观察者是在相应的事件发生以前传递消息,因此通知的时间和实际事件发生的时间之间可能存在偏差。若是须要精确时间控制,你可使用休眠和唤醒通知来帮助你校对实际发生事件的时间。

    由于当你运行run loop时定时器和其它周期性事件常常须要被传递,撤销run loop也会终止消息传递。典型的例子就是鼠标路径追踪。由于你的代码直接获取到消息而不是经由程序传递,所以活跃的定时器不会开始直到鼠标追踪结束并将控制权交给程序。

    Run loop能够由run loop对象显式唤醒。其它消息也能够唤醒run loop。例如,添加新的非基于端口的源会唤醒run loop从而能够当即处理输入源而不须要等待其余事件发生后再处理。

    从这个事件队列中能够看出:

    ①若是是事件到达,消息会被传递给相应的处理程序来处理, runloop处理完当次事件后,run loop会退出,而无论以前预约的时间到了没有。你能够从新启动run loop来等待下一事件。

    ②若是线程中有须要处理的源,可是响应的事件没有到来的时候,线程就会休眠等待相应事件的发生。这就是为何run loop能够作到让线程有工做的时候忙于工做,而没工做的时候处于休眠状态。

四。何时使用runloop

仅当在为你的程序建立辅助线程的时候,你才须要显式运行一个run loop。Run loop是程序主线程基础设施的关键部分。因此,Cocoa和Carbon程序提供了代码运行主程序的循环并自动启动run loop。IOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)做为程序启动步骤的一部分,它在程序正常启动的时候就会启动程序的主循环。相似的,RunApplicationEventLoop函数为Carbon程序启动主循环。若是你使用xcode提供的模板建立你的程序,那你永远不须要本身去显式的调用这些例程。

对于辅助线程,你须要判断一个run loop是不是必须的。若是是必须的,那么你要本身配置并启动它。你不须要在任何状况下都去启动一个线程的run loop。好比,你使用线程来处理一个预先定义的长时间运行的任务时,你应该避免启动run loop。Run loop在你要和线程有更多的交互时才须要,好比如下状况:

  1. 使用端口或自定义输入源来和其余线程通讯
  2. 使用线程的定时器
  3. Cocoa中使用任何performSelector…的方法
  4. 使线程周期性工做

若是你决定在程序中使用run loop,那么它的配置和启动都很简单。和全部线程编程同样,你须要计划好在辅助线程退出线程的情形。让线程天然退出每每比强制关闭它更好。

相关文章
相关标签/搜索