一、概念
Run loops 是与线程相关的基础构造部分。
一个 run loop就是一个事件处理循环,调配任务和处理输入事件。
run loop的目标就是,有任务的时候保持忙碌,没有任务时休眠。
Cocoa和Core Foundation都提供run loop对象帮助我们配置和管理run loop。
应用不需要显示的创建run loop对象;每个线程,包括主线程都有与之相关的run loop对象。
主线程在程序启动后,会自行创建run loop对象,并运行;只有次线程需要显示的创建run loop对象,并运行。
1.事件来源
run loop有两种事件来源:输入源和定时源。
输入源传递异步消息,消息通常来自其他线程或者不同应用。
定时源传递同步消息,在安排的时间或重复的时间间隔发生。
两种源都使用应用特定的处理路径处理事件。
1)输入源
当你创建输入源后,需要把它分配给一种run loop模式
run loop模式影响在给定时刻哪种输入源会被监控。
输入源的种类
基于端口的源,可定制输入源,cocoa执行函数输入源。
基于端口的源 监控应用相关的端口。
可定制输入源 监控定制的事件来源。
cocoa执行函数输入源 cocoa定义了一个定制的输入源,允许你在任何线程执行函数选择器
2)定时源
2.run loop模式
run loop模式是一个集合,这个集合包括需要监控的输入源和定时源以及需要通知的run loop观察者。
每当你运行run loop时,要给它配一种模式。只有与这种模式相关的事件来源会被监控,并允许发送消息;只有与这种模式相关的runloop观察者会被通知。其他模式相关的事件来源会只有在其模式配给run loop时,才会起作用,否则暂停。
3.run loop观察者
可以创建以下事件相关的观察者
run loop入口
run loop将要处理定时源时
run loop将要处理输入源时
run loop将要休眠时
run loop已被唤醒, 且在它处理唤醒它的事件之前
run loop终止
二、何时使用run loop
只有创建次线程时,才需要显示地运行run loop。
没有必要在所有的情况下启动线程的run loop。例如,用线程执行某个长时间运行并且已经约定好的任务,就应该避免启动runloop。
你需要频繁或者更多地与线程交互,就需要使用run loop。
例如,在以下情况需要启动run loop:
使用端口源或者可定制的输入源与其他线程通信
使用定时器
在cocoa应用中使用 类似performSelector的方法
使用线程执行周期性任务
三、使用run loop对象
run loop对象提供主要的接口,向run loop添加输入源,定时器,观察者以及运行run loop。
每个线程都有一个关联的,单独的run loop对象。
在cocoa中,这个关联对象是NSRunLoop对象;在Carbon或BSD应用中,是CFRunLoopRef类型的指针。
1.获取run loop对象
1)
NSRunLoop*runLoop = [NSRunLoop currentRunLoop];
CFRunLoopRefrunLoopRef = [runLoop getCFRunLoop];
2)
CFRunLoopRefrunLoopRef = CFRunLoopGetCurrent();
2.配置run loop
在次线程上运行run loop之前,必须至少添加一个输入源或者定时器。
如果run loop没有任何来源监控,当你试着运行它时就会立刻退出。
run loop观察者必须用Core Foundation创建。
创建观察者的代码示例
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//get runloop object
NSRunLoop*runLoop = [NSRunLoop currentRunLoop];
//create arun loop observer and attach it to the run loop
CFRunLoopObserverContext context = {0,self,NULL,NULL,NULL};
CFRunLoopObserverRef runLoopObserver =CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopBeforeSources, YES, 0,&runLoopObserverCallBack,&context);
if(runLoopObserver)
{
CFRunLoopRefrunLoopRef = [runLoop getCFRunLoop];
CFRunLoopAddObserver(runLoopRef, runLoopObserver,kCFRunLoopDefaultMode);
}
while(!self.isCancelled && [runLooprunMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]]);
[pool drain];
}
3.启动run loop
有几种方法启动run loop,包括以下几种:
- 无条件的
- 预设时间
- 特定模式
预设时间是一种更好的选择。runloop运行,知道接收到一个事件或者预设的时间过期。如果是接到事件,就把时间交给处理器处理,然后退出runloop,你可以重新启动run loop来处理下一个事件;如果是分派的时间过期,你可以简单地重启runloop,使用时间去做其他任何事情。
特定模式也是一种好的选择。模式与预设时间并不冲突,可以同时使用。
示例代码
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//get runloop object
NSRunLoop*runLoop = [NSRunLoop currentRunLoop];
//create arun loop observer and attach it to the run loop
CFRunLoopObserverContext context = {0,self,NULL,NULL,NULL};
CFRunLoopObserverRef runLoopObserver =CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopBeforeSources, YES, 0,&runLoopObserverCallBack,&context);
if(runLoopObserver)
{
CFRunLoopRefrunLoopRef = [runLoop getCFRunLoop];
CFRunLoopAddObserver(runLoopRef, runLoopObserver,kCFRunLoopDefaultMode);
}
BOOL done =NO;
do {
//start runloop but return after each source is handled
SInt32result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
// If asource explicitly stopped the run loop, or if there are no
// sources or timers, go ahead and exit.
if (result== kCFRunLoopRunStopped || result == kCFRunLoopRunStopped)
{
done =YES;
}
} while(!done);
// while(!self.isCancelled && [runLooprunMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]]);
[pool drain];
}
4.退出run loop
有两种方法:
- 设定一个时限运行run loop
- 停止 run loop
使用CFRunLoopStop方法退出run loop,效果与设定时限类似。区别是无条件启动run loop时,不可以使用。
5.线程安全与run loop对象
Core Foundation的方法都是线程安全的。可以在任何线程调用。如果在配置过的run loop执行操作,最好是在拥有该runloop的线程中操作。
NSRunLoop是线程不安全的。使用NSRunLoop配置run loop时,最好在该runloop所属的线程中操作,否则结果不可预期或者崩溃。
四、配置run loop源
暂时搁置,有需要再研读。