RunLoop
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RunLoop相关的知识,希望对你有一定的参考价值。
RunLoop的概念
RunLoop:从字面上看是运行循环、跑圈
其实RunLoop内部是一个类似于do while()循环,在程序运行时它就像是一直在画着圈儿,监听着事件
一个 run loop 是用来在线程上管理事件异步到达的基础设施。一个 run loop 为 线程监测一个或多个事件源。当事件到达的时候,系统唤醒线程并调度事件到 run loop,然后分配给指定程序。如果没有事件出现和准备处理,run loop 把线程置于休 眠状态。
Run Loop 模式
Run loop 模式是所有要监视的输入源和定时源以及要通知的 run loop 注册观察 者的集合。每次运行你的 run loop,你都要指定(无论显示还是隐式)其运行个模 式。在 run loop 运行过程中,只有和模式相关的源才会被监视并允许他们传递事件 消息。(类似的,只有和模式相关的观察者会通知 run loop 的进程)。和其他模式关 联的源只有在 run loop 运行在其模式下才会运行,否则处于暂停状态。
通常在你的代码中,你可以通过指定名字来标识模式。Cocoa 和 Core foundation 定义了一个默认的和一些常用的模式,在你的代码中都是用字符串来标识这些模式。 当然你也可以给模式名称指定一个字符串来自定义模式。虽然你可以给模式指定任意 名字,但是模式的内容则不能是任意的。你必须添加一个或多个输入源,定时源或者 run loop 的观察者到你新建的模式中让他们有价值。
通过指定模式可以使得 run loop 在某一阶段过滤来源于源的事件。大多数时候,
run loop 都是运行在系统定义的默认模式上。但是模态面板(modal panel)可以运 行在 “modal”模式下。在这种模式下,只有和模式面板相关的源才可以传递消息给 线程。对于辅助线程,你可以使用自定义模式在一个时间周期操作上屏蔽优先级低的 源传递消息
输入源
输入源异步的发送消息给你的线程。事件来源取决于输入源的种类:基于端口的 输入源和自定义输入源。基于端口的输入源监听程序相应的端口。自定义输入源则监 听自定义的事件源。至于 run loop,它不关心输入源的是基于端口的输入源还是自 定义的输入源。系统会实现两种输入源供你使用。两类输入源的区别在于如何显示: 基于端口的输入源由内核自动发送,而自定义的则需要人工从其他线程发送。
当你创建输入源,你需要将其分配给 run loop 中的一个或多个模式。模式只会 在特定事件影响监听的源。大多数情况下,run loop 运行在默认模式下,但是你也 可以使其运行在自定义模式。若某一源在当前模式下不被监听,那么任何其生成的消 息只在 run loop 运行在其关联的模式下才会被传递。
Run Loop 观察者
源是合适的同步或异步事件发生时触发,而 run loop 观察者则是在 run loop 本
身运行的特定时候触发。你可以使用 run loop 观察者来为处理某一特定事件或是进
入休眠的线程做准备。你可以将 run loop 观察者和以下事件关联:
Run loop 入口
Run loop 何时处理一个定时器
Run loop 何时处理一个输入源
Run loop 何时进入睡眠状态
Run loop 何时被唤醒,但在唤醒之前要处理的事件
Run loop 终止
你可以给 run loop 观察者添加到 Cocoa 和 Carbon 程序里面,但是如果你要定义
观察者并把它添加到 run loop 的话,那就只能使用 Core Fundation 了。为了创建一 个 run loop 观察者,你可以创建一个 CFRunLoopObserverRef 类型的实例。它会追踪 你自定义的回调函数以及其它你感兴趣的活动。
和定时器类似,run loop 观察者可以只用一次或循环使用。若只用一次,那么在 它启动后,会把它自己从 run loop 里面移除,而循环的观察者则不会。你在创建 run loop 观察者的时候需要指定它是运行一次还是多次。
Run Loop 的事件队列
每次运行 run loop,你线程的 run loop 对会自动处理之前未处理的消息,并通
知相关的观察者。具体的顺序如下:
-
通知观察者 run loop 已经启动
-
通知观察者任何即将要开始的定时器
-
通知观察者任何即将启动的非基于端口的源
-
启动任何准备好的非基于端口的源
-
如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
-
通知观察者线程进入休眠
-
将线程置于休眠直到任一下面的事件发生:
某一事件到达基于端口的源
定时器启动
Run loop 设置的时间已经超时 run loop 被显式唤醒 -
通知观察者线程将被唤醒。
-
处理未处理的事件
如果用户定义的定时器启动,处理定时器事件并重启 run loop。进入步骤 2 如果输入源启动,传递相应的消息
如果 run loop 被显式唤醒而且时间还没超时,重启 run loop。进入步骤 2 10. 通知观察者 run loop 结束。
因为定时器和输入源的观察者是在相应的事件发生之前传递消息,所以通知的时 间和实际事件发生的时间之间可能存在误差。如果需要精确时间控制,你可以使用休 眠和唤醒通知来帮助你校对实际发生事件的时间。
因为当你运行 run loop 时定时器和其它周期性事件经常需要被传递,撤销 run loop 也会终止消息传递。典型的例子就是鼠标路径追踪。因为你的代码直接获取到 消息而不是经由程序传递,因此活跃的定时器不会开始直到鼠标追踪结束并将控制权 交给程序。
Run loop 可以由 run loop 对象显式唤醒。其它消息也可以唤醒 run loop。例如, 添加新的非基于端口的源会唤醒 run loop 从而可以立即处理输入源而不需要等待其 他事件发生后再处理。
RunLoop对象
ios中有两套API来访问和使用RunLoop
Foundation框架中的NSRunLoop
Core Foundation中的CFRunLoop
Run loop 对象 供了添加输入源,定时器和 run loop 的观察者以及启动 run loop 的接口。每个线程都有唯一的与之关联的 run loop 对象。在 Cocoa 中,该对象是 NSRunLoop 类的一个实例;而在 Carbon 或 BSD 程序中则是一个指向 CFRunLoopRef 类 型的指针。
获得RunLoop对象
为了获得当前线程的 run loop,你可以采用以下任一方式:
-
在 Cocoa 程序中,使用 NSRunLoop 的 currentRunLoop 类方法来检索一个
NSRunLoop 对象。
-
使用CFRunLoopGetCurrent函数。
虽然它们并不是完全相同的类型,但是你可以在需要的时候从 NSRunLoop 对象中 获取 CFRunLoopRef 类型。NSRunLoop 类定义了一个 getCFRunLoop 方法,该方法返回 一个可以传递给 Core Foundation 例程的 CFRunLoopRef 类型。因为两者都指向同一 个 run loop,你可以在需要的时候混合使用 NSRunLoop 对象和 CFRunLoopRef 不透明 类型。配置RunLoop在你在辅助线程运行 run loop 之前,你必须至少添加一输入源或定时器给它。 如果 run loop 没有任何源需要监视的话,它会在你启动之际立马退出。关于如何添 加源到 run loop 里面的例子,参阅”配置 Run Loop 源”。除了安装源,你也可以添加 run loop 观察者来监视 run loop 的不同执行阶段情 况。为了给 run loop 添加一个观察者,你可以创建 CFRunLoopObserverRef 不透明类 型,并使用 CFRunLoopAddObserver 将它添加到你的 run loop。Run loop 观察者必须 由 Core foundation 函数创建,即使是 Cocoa 程序
以上是关于RunLoop的主要内容,如果未能解决你的问题,请参考以下文章