什么是RunLoop
RunLoop从字面意思上理解,就是运行循环的意思,它的基本作用就是保持程序的持续运行,处理App中的比如触摸、定时、Selector等等事件。RunLoop可以节省CPU的资源,提高程序的性能等等作用。
如果没有RunLoop
1. int main(int agrc,char *argv[]){
2. NSLog(@"execute main function");//程序开始
3. return 0;//程序结束
4. }
如果有了RunLoop
1. int main(int argc,char *argv[]){
2. BOOL running = YES;
3. do{
4. //处理各种任务,处理各种事件
5. while(running);
6. return 0;
7. }}
- 在有了RunLoop的情况下,由于main函数里面启动了个RunLoop,所以程序并不会马上推出,保持持续运行状态
main函数中的RunLoop
1. int main(int agrc,char *argv[]){
2. @autoreleasepool{
3. return UIApplicationMain(argc,argv,nil,NSStringFromClass*([AppDelegate class]));
4. }
5. }
- UIApplicationMain函数内部就启动了一个RunLoop
- 所以UIApplicationMain函数一直没有返回,保持了程序的持续运行
- 这个默认启动的RunLoop是跟主线程相关连的
RunLoop对象
- iOS中有两套API来访问和使用RunLoop
- Foundation -> NSRunLoop
- Core Foundation -> CFRunLoopRef
- NSRunLoop和CFRunLoop都代表着RunLoop对象
- NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)
- 每一条线程都有唯一的一个与之对应的RunLoop对象
- 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
- RunLoop在第一次获取时创建,在线程结束时销毁
获取RunLoop对象
- Fundation
- [NSRunLoop currentRunLoop];//获取当前线程的RunLoop对象
- [NSRunLoop mainRunLoop];//获取主线程的RunLoop对象
- Core Foundation
- CFRunLoopGetCurrent();//获取当前线程的RunLoop对象
- CFRunLoopGetMain();//获得主线程的RunLoop对象
RunLoop的相关类
- Core Foundation中关于RunLoop的5个类
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef
CFRunLoopModeref
- CFRunLoopModeRef代表RunLoop的运行模式
- 一个RunLoop包含着若干个Mode,每个Mode又包含若干个Source、Timer、Observer
- 每次RunLoop启动时,只能指定其中一个Mode,这个Mode被称作CurrentMode
- 如果需要切换Mode,只能退出Loop,在重新制定一个Mode进入
- 这样做主要是为了分隔开不同组的Source/Timer/Observe,让其互不影响
- 系统默认注册了5个Mode
- kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode: 界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
- UIInitializationRunLoopMode: 在刚启动App时第一次进入的第一个Mode,启动完成之后就不再使用
- GSEventReceiveRunLoopMode: 接受系统事件的内部Mode,通常用不到
- kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
CFRunLoopSourceRef
- CFRunLoopSourceRef是事件源(输入源)
- Source0:非基于Port
- Source1:基于Port
CFRunLoopTimerRef
- CFRunLoopTimerRef是基于时间的触发器
- 基本上说的就是NSTimer
CFRunLoopObserRef
-
CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
-
可以监听的时间点有一下几个
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0),//即将进入Loop kCFRunLoopBeforeTimers = (1UL << 1),//即将处理Timer kCFRunLoopBeforeSources = (1UL << 2),//即将处理Source kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠 kCFRunLoopAfterWaiting = (1UL << 6),//刚从休眠中唤醒 kCFRunLoopExit = (1UL << 7),//即将退出Loop kCFRunLoopAllActivities = 0x0FFFFFFFU };
RunLoop处理逻辑
官方版
- 每次运行RunLoop,你的线程的RunLoop会自动处理之前未处理的消息,并通知相关的观察者。具体如下:
- 通知观察者run loop已经启动
- 通知观察者任何即将要开始的定时器
- 通知观察者任何即将启动的非基于端口的源
- 启动任何准备好的非基于端口的源
- 如果基于端口的源准备好并处于等待状态,立即启动,并且进入步骤9
- 通知观察者线程进入休眠
- 将线程置于休眠直到任意下面的事情发生:
- 某一事件到达基于端口的源
- 定时器启动
- run loop设置的时间已经超时
- run loop被娴熟唤醒
- 通知观察者线程将被唤醒
- 处理未处理的事件
- 如果用户定义的定时器被启动,处理定时器事件并重启。进入步骤2
- 如果处理输入源启动,传递相应的消息
- 如果run loop被显示唤醒而且时间还没超过,重启run loop。进入步骤2
- 通知观察者run loop结束
网友整理版
RunLoop小节
- RunLoop整理:
- 从字面意思看:运行循环、跑圈,其实它内部就是do-while循环,就这个循环内部不断地处理各种任务(比如Source、Timer、Observer)。
- 一个线程对应一个RunLoop,主线程的RunLoop默认已经启动,子现场的RunLoop得手动启动。
- RunLoop只能选择一个模式Mode启动,如果当前Mode中没有任何Source、Timer、Observer,那么就直接退出RunLoop
- 自动释放池什么时候释放?(在RunLoop睡眠之前释放(kCFRunLoopBeforeWaiting))
- RunLoop如何使用于应用场景
- 让一个子线程不进入消亡状态,等待从其它线程发来消息,处理其它事情
- 在子线程中开启一个定时器
- 在子线程中长期监控一些行为
- 可以控制定时器在哪种模式下运行
- 可以让某些事件(行为、任务)在特定模式下执行
- 可以添加Observer监听RunLoop的状态,比如监听点击事件的处理(在所有点击事件致前做点事情)
网友评论