1. 基本解析
1.它是一个循环,你的线程进入并使用它 来运行响应输入事件的事件处理程序,Run loop 接收输入事件来自两种不同的来源:输入源(input source)和定时源 (timer source)。输入源传递异步事件,通常消息来自于其他线程或程序。定时源 则传递同步事件,发生在特定时间或者重复的时间间隔
图 3-1 显示了 run loop 的概念结构以及各种源
Runloop的模式包括:常用的有NSDefultRunLoopMode:主要是用在默认情况下
UITrackingRunLoopMode:
用在对UI界面追踪模式
NSRunLoopCommonModes:两种情况都包含的情况
NSModalPanelRunLoopMode(Cocoa):处理modal panels事件。
NSConnectionReplyMode(Cocoa):处理NSConnection对象相关事件,系统内部使用,用户基本不会使用
2. 输入源
输入源分两种:基于端口的输入源监听程序相应的端口,自定义输入源监听自定义事件,两类输入源的区别在于如何显示: 基于端口的输入源由内核自动发送,而自定义的则需要人工从其他线程发送;当你创建输入源,你需要将其分配给 run loop 中的一个或多个模式
2.1 基于端口的输入源
在 Cocoa 里面你从来不需要直接创建输入源。你只要简单的创建端口 对象,并使用 NSPort 的方法把该端口添加到 run loop。端口对象会自己处理创建和 配置输入源。在 Core Foundation,你必须人工创建端口和它的 run loop 源。
2.2 自定义输入源
为了创建自定义输入源,必须使用 Core Foundation 里面的 CFRunLoopSourceRef
类型相关的函数来创建。你可以使用回调函数来配置自定义输入源。Core Fundation 会在配置源的不同地方调用回调函数,处理输入事件,在源从 run loop 移除的时候 清理它。
2.3 Cocoa 执行 Selector 的源
Cocoa 定义了自定义输入源,允许你在任何线程执行selector,表 3-2 列出了 NSObject 中可在其它线程执行的 selector3. 定时源
定时源在预设的时间点同步方式传递消息。定时器是线程通知自己做某事的一种方法,尽管定时器可以产生基于时间的通知,定时器也和你的 run loop 的特定模式相关,你可以配置定时器工作仅一次还是重复工作,重复工作定时器会基于安排好的时 间而非实际时间调度它自己运行;
如果定时器被延迟以至于它错过了一个或多个触发时间,那么定时器会 在下一个最近的触发时间启动,而后面会按照触发间隔正常执行。
3. Run Loop观察者
源是合适的同步或异步事件发生时触发,而 run loop 观察者则是在 run loop 本身运行的特定时候触发,你可以使用 run loop 观察者来为处理某一特定事件或是进 入休眠的线程做准备,你可以将 run loop 观察者和以下事件关联:
- Run loop 入口
- Run loop 何时处理一个定时器
- Run loop 何时处理一个输入源
- Run loop 何时进入睡眠状态
- Run loop 何时被唤醒,但在唤醒之前要处理的事件
- Run loop 终止
4. Run Loop 的事件队列
每次运行 run loop,你线程的 run loop 对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:
- 通知观察者 run loop 已经启动
- 通知观察者任何即将要开始的定时器
- 通知观察者任何即将启动的非基于端口的源
- 启动任何准备好的非基于端口的源
- 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
- 通知观察者线程进入休眠
- 将线程置于休眠直到任一下面的事件发生:
某一事件到达基于端口的源
- 定时器启动
- Run loop 设置的时间已经超时
- run loop 被显式唤醒
- 通知观察者线程将被唤醒。
- 处理未处理的事件
- 如果用户定义的定时器启动,处理定时器事件并重启 run loop。进入步骤 2
- 如果输入源启动,传递相应的消息
- 如果 run loop 被显式唤醒而且时间还没超时,重启 run loop。进入步骤 2
- 通知观察者 run loop 结束。
5. 使用 Run Loop 对象
5.1 获得RunLoop对象
- 在 Cocoa 程序中,使用 NSRunLoop 的 currentRunLoop 类方法来检索一个NSRunLoop 对象。
- 在Core Foundation中使用CFRunLoopGetCurrent函数
5.2 配置 Run Loop
当你在辅助线程运行 run loop 之前,你必须至少添加一输入源或定时器给它。 如果 run loop 没有任何源需要监视的话,它会在你启动之际立马退出。
除了安装源,你也可以添加 run loop 观察者来监视 run loop 的不同执行阶段情 况。
为了给 run loop 添加一个观察者,你可以创建 CFRunLoopObserverRef 不透明类型,并使用 CFRunLoopAddObserver 将它添加到你的 run loop
Run loop 观察者必须由 Core foundation 函数创建,即使是 Cocoa 程序
下面是创建观察者的例子:
- (void)threadMain {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSRunLoop *myRunloop = [NSRunLoop currentRunLoop];
// 创建一个run loop observer并且添加到runloop上
CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
CFRunLoopRef cfRunloop = [myRunloop getCFRunLoop];
CFRunLoopAddObserver(cfRunloop, observer, kCFRunLoopDefaultMode);
// 创建定时器
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doFireTimer) userInfo:nil repeats:YES];
[myRunloop run];
});
}
- (void)doFireTimer {
NSInteger loopCount = 10;
do {
NSLog(@"%ld",loopCount);
loopCount--;
}while (loopCount);
}
void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
NSLog(@"%@,%@",info,[NSThread currentThread]);
}