关于Runloop的使用(该文章仅作为个人笔记,原理文章请查看尾部《相关链接》)

 

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模型:

苹果公开提供的Mode有两个:KCFRunLoopDefalutMode(NSDefaultRunLoopMode)和UITrackingRunLoopMode
1、NSDefaultRunLoopMode 默认,空闲状态
2、UITrackingRunLoopMode 滑动ScrollView
3、UIInitializationRunLoopMode 私有,App启动时
4、NSRunLoopCommonModes 默认包括上面1、2条

注意:这里又一个概念叫“CommonModes”:一个Mode可以将自己标记为“Common”属性;

每当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的状态改变,以下为可以监听的时间点:


代码示例:

  1. // 当手指触摸控制器View的时候,调用该方法  
  2. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event  
  3. {  
  4.     // 创建Observer  
  5.     /** 
  6.      *  参数1: 指定如果给Observer分配存储空间 
  7.      *  参数2: 需要监听的状态类 
  8.      *       kCFRunLoopEntry = (1UL << 0),         即将启动(进入)的时候 
  9.      *       kCFRunLoopBeforeTimers = (1UL << 1),  即将处理timer事件 
  10.      *       kCFRunLoopBeforeSources = (1UL << 2), 即将处理source事件 
  11.      *       kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入睡眠 
  12.      *       kCFRunLoopAfterWaiting = (1UL << 6),  RunLoop被唤醒 
  13.      *       kCFRunLoopExit = (1UL << 7),          RunLoop退出 
  14.      *       kCFRunLoopAllActivities = 0x0FFFFFFFU 监听所有状态 
  15.      * 
  16.      */  
  17.     CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {  
  18.         switch (activity) {  
  19.             case kCFRunLoopEntry:  
  20.                 NSLog(@"即将进入RunLoop");  
  21.                 break;  
  22.             case kCFRunLoopBeforeTimers:  
  23.                 NSLog(@"即将处理timer");  
  24.                 break;  
  25.             case kCFRunLoopBeforeSources:  
  26.                 NSLog(@"即将处理source");  
  27.                 break;  
  28.             case kCFRunLoopBeforeWaiting:  
  29.                 NSLog(@"即将进入睡眠");  
  30.                 break;  
  31.             case kCFRunLoopAfterWaiting:  
  32.                 NSLog(@"RunLoop刚从睡眠中唤醒");  
  33.                 break;  
  34.             case kCFRunLoopExit:  
  35.                 NSLog(@"RunLoop即将退出");  
  36.                 break;  
  37.             default:  
  38.                 break;  
  39.         }  
  40.     });  
  41.       
  42.     // 给主线程的RunLoop添加一个观察者,要监听的是RunLoop的哪种运行模式  
  43.     /** 
  44.      *  参数1: 需要给哪个RunLoop添加观察者 
  45.      *  参数2: 需要添加的Observer对象 
  46.      *  参数3: 在哪种模式下可以监听 kCFRunLoopDefaultMode == NSDefaultRunLoopMode 
  47.      */  
  48.     CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);  
  49.       
  50.     // 释放对象  
  51.     CFRelease(observer);  
  52.       
  53.     [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];  
  54.       
  55. }  
  56.   
  57. - (void)show{  
  58.     NSLog(@"%s", __func__);  
  59. }  
  60.   
  61. @end  


打印结果:






RunLoop处理逻辑示意图:

RunLoop处理逻辑步骤解释:




相关参考资料:

使用RunLoop对象

深入理解RunLoop-CocoaChina



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值