RunLoop的概念模型:
这种模型通常被称作 Event Loop。 Event Loop在很多系统和框架里都有实现,比如 Node.js的事件处理,比如 Windows程序的消息循环,再比如 OSX/iOS里的 RunLoop。实现这种模型的关键点在于:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。
模型处理机制关键:
1、如何管理事件/消息;
2、如何让线程在没有处理消息时休眠以避免资源占用、并在消息到来时立刻被唤醒
所以,Runloop实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供一个入口函数来执行上面的Event Loop的逻辑。线程执行这个函数后,就会一直处于这个函数内部“接受消息-->等待-->处理”的循环中,知道循环结束(比如传入quit的消息),函数返回。
在OSX/IOS系统中,提供了两个这样的对象,NSRunLoop和CFRunLoopRef。
1、CFRunLoopRef是在CoreFundation框架内的,它提供了纯C函数的API,所有这些API都是线程安全的。
2、NSRunLoop是基于CFRunLoopRef的封装,他提供了面向对象的API,但是这些API不是线程安全的
线程和RunLoop关系:
1、线程和RunLoop是一一对应的,其关系保存在一个全局的Dictionary里。
2、线程刚创建时并没有RunLoop,如果你不主动获取,那么它一直都不会有。
3、RunLoop的创建是在第一次获取时,RunLoop的销毁是发生在线程结束时。
4、只能在某个线程内部获取其currentRunLoop(主线程除外)
RunLoop对外接口:
在CoreFundation里面关于RunLoop的5个类:
1、CFRunLoopRef
2、CFRunLoopModeRef(该类并没有对外暴露,只是通过CFRunLoopRef的接口进行了封装)
3、CFRunLoopSourceRef
4、CFRunLoopTimerRef
5、CFRunLoopObserverRef
关系图如下:
1、一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer。
2、每次调用RunLoop的主函数时,只能指定其中一个Mode,这个Mode又被称为CurrentMode。
3、如需切换Mode,只能退出Loop,再重新指定一个Mode进入;这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响。
4、上面的Source/Timer/Observer统称Modde Item;(一个Item可以同时加入多个Mode,但同一个Item被加入同一个Mode时,是不会有效果的。)
RunLoopMode模型:
每当RunLoop的内容发生变化时,RunLoop都会将_CommonModelItems里的Source/Observer/Timer同步到具有“Common”标记的所有Model里
获取RunLoop对象:(获取RunLoop即创建了RunLoop,因为是懒加载方式)
1、Foundation框架:
[NSRunLoop currentRunLoop];//获取当前线程的RunLoop对象
[NSRunLoop mainRunLoop];//获取主线程的RunLoop对象
2、Core Foundation框架:
CFRunLoopGetCurrent();//获取当前线程的RunLoop对象
CFRunLoopGetMain();//获取主线程的RunLoop对象
CFRunLoopSourceRef事件源(输入源):
1、Source0:非基于Port的,用于用户主动触发的事件
2、Source1:基于Port的,通过内核和其他线程相互发送消息
CFRunLoopTimerRef定时器:
1、CFRunLoopTimerRef是基于时间的触发器,基本上说的就是NSTimer,它会受到RunLoop的mode的影响
2、GCD的定时器不受RunLoop的mode的影响
CFRunLoopObserverRef观察者:
能够监听RunLoop的状态改变,以下为可以监听的时间点:
代码示例:
- // 当手指触摸控制器View的时候,调用该方法
- - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
- {
- // 创建Observer
- /**
- * 参数1: 指定如果给Observer分配存储空间
- * 参数2: 需要监听的状态类
- * kCFRunLoopEntry = (1UL << 0), 即将启动(进入)的时候
- * kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
- * kCFRunLoopBeforeSources = (1UL << 2), 即将处理source事件
- * kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入睡眠
- * kCFRunLoopAfterWaiting = (1UL << 6), RunLoop被唤醒
- * kCFRunLoopExit = (1UL << 7), RunLoop退出
- * kCFRunLoopAllActivities = 0x0FFFFFFFU 监听所有状态
- *
- */
- CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
- switch (activity) {
- case kCFRunLoopEntry:
- NSLog(@"即将进入RunLoop");
- break;
- case kCFRunLoopBeforeTimers:
- NSLog(@"即将处理timer");
- break;
- case kCFRunLoopBeforeSources:
- NSLog(@"即将处理source");
- break;
- case kCFRunLoopBeforeWaiting:
- NSLog(@"即将进入睡眠");
- break;
- case kCFRunLoopAfterWaiting:
- NSLog(@"RunLoop刚从睡眠中唤醒");
- break;
- case kCFRunLoopExit:
- NSLog(@"RunLoop即将退出");
- break;
- default:
- break;
- }
- });
- // 给主线程的RunLoop添加一个观察者,要监听的是RunLoop的哪种运行模式
- /**
- * 参数1: 需要给哪个RunLoop添加观察者
- * 参数2: 需要添加的Observer对象
- * 参数3: 在哪种模式下可以监听 kCFRunLoopDefaultMode == NSDefaultRunLoopMode
- */
- CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
- // 释放对象
- CFRelease(observer);
- [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
- }
- - (void)show{
- NSLog(@"%s", __func__);
- }
- @end
打印结果:
RunLoop处理逻辑示意图:
RunLoop处理逻辑步骤解释:
相关参考资料: