runloop从字面意思就是运行循环,跑圈。
runloop基本作用:
*保持程序的 持续运行
*处理App种的各种事件,比如触摸时间、定时器事件、Selector事件。
*节省CPU资源,提高程序性能:该做事时做事,该休息时休息。
*。。。。。。
如果说没又runloop的话,那么程序就会立即结束,代码运行到第三行程序就结束。
1.intmain(intargc,char*argv[]) {
2.NSLog(@"ZhouXiaoShuai");
3.return 0;
4.}
有RunLoop的话,main函数里面启动了RunLoop,那么程序并不会马上退出,保持持续运行状态。
1.intmain(intargc,char*argv[]) {
2.BOOLrunning =YES;
3.do{
4.//执行各种任务,处理各种事件
5.// ......
6.}while(running);
7.return 0;
8.}
main函数种的RunLoop
第14行代码的UIApplicationMain函数内部就启动了一个RunLoop,所以UIApplicationMain函数一直没有返回,保持了程序的持续运行,这个默认启动的RunLoop是跟主线程相关联的。
iOS中想访问和使用RunLoop的话有两套API
Foundation中的NSRunLoop
Core Foundation中的CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)
RunLoop与线程
每条线程都有唯一的一个与之对应的RunLoop对象。
主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建。
RunLoop在第一次获取时创建,在线程结束时销毁。
Foundation
[NSRunLoop currentRunLoop]; //获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; //获得主线程的RunLoop对象
Core Foundation
CFRunLoopGetCurrent(); //获得当前线程的RunLoop对象
CFRunLoopGetMain(); //获得主线程的RunLoop对象
RunLoop相关类
CoreFoundation中关于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/Observer,让其互不影响。
CFRunLoopModeRef
系统默认注册了5个Mode:
kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行。
UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响。
UIInitializationRunLoopMode:在刚启动App时第进入的第一个Mode,启动完成后就不再使用。
GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode。
CFRunLoopSourceRef
CFRunLoopSourceRef是事件源(输入源)
以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
现在的分法
Source0:非基于Port的
Source1:基于Port的
CFRunLoopTimerRef
CFRunLoopTimerRef是基于时间的触发器
基本上说的就是NSTimer
CFRunLoopObserverRef
CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变,可以监听的时间点有以下几个。
RunLoop处理逻辑-官方版
RunLoop处理逻辑-官方版
RunLoop的事件队列:
每次运行runloop,你线程的runloop对会自动处理之前未处理的消息,并通知相关的观察者,具体的顺序如下:
1.通知观察者run loop已经启动。
2.通知观察者任何即将要开始的定时器。
3.通知观察者任何即将启动的非基于端口的源。
4.启动任何准备好的非基于端口的源。
5.如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
6.通知观察者线程进入休眠。
7.将线程置于休眠直到下面的事件发生:
- 某一事件达到基于端口的源。
- 定时器启动。
- RunLoop设置的时间已经超时。
- RunLoop被显式唤醒。
8.通知观察者线程将被唤醒。
9.处理未处理的事件
- 如果用户定义的定时器启动,处理定时器事件并重启runloop 进入步骤2
- 如果输入源启动,传递相应的消息。
- 如果runloop被显式唤醒而且时间还没超时,重启runloop 进入步骤2
10.通知观察者runloop结束。