一、是啥
runloop,运行循环,程序运行的过程中循环做一些事情。
应用范围:
- 定时器(timer)、PerformSelector、
- GCD Async Main Queue
- 事件响应、手势识别、界面刷新
- 网络请求
- AutoreleasePool
main.m里的main方法里就开启了Runloop,可以让程序不会马上退出,而是保持运行状态
int main(int argc, char * argv[]) {
@autoreleasepool {
//开启了runloop
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])
}
}
也相当于下面的伪代码
int main(int argc, char * argv[]) {
@autoreleasepool {
int retVal = 0;
do{
//睡眠中等待消息
int message = sleep_and_wait();
//处理消息
retVal = process_message(message);
}while(0==retVal)
}
}
二、基本作用
1、保持程序持续运行
2、处理App中的各种事件,比入触摸事件,定时器事件
3、节省CPU资源,提高程序性能:该做事时做事,该休息休息
三、Runloop与线程之间的关系
1、每条线程都有唯一的一个与之对应的Runloop对象
2、Runloop保存在一个全局的Dictionary里,线程作为key,Runloop作为value
3、线程刚创建时并没有Runloop对象,Runloop会在第一次获取它时创建
4、Runloop会在线程结束时销毁
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));//线程作为key,从字典里取出loop
__CFUnlock(&loopsLock);
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);//没有loop,就创建一个
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);//存loop存到字典
loop = newLoop;
}
上面是源码的一部分,说明了1、2、3
四、获取Runloop对象
iOS中有2套API来访问和使用RunLoop.
Foundation: NSRunloop
Core Foundation: CFRunloopRef
NSRunloop和CFRunloopRef都代表NSRunloop对象,是等价的。
NSRunloop是基于CFRunloopRef的一层OC包装。
[NSRunLoop currentRunLoop];//获取当前线程的runloop
[NSRunLoop mainRunLoop];//获取主线程的runloop
五、RunLoop相关的类
主要的5个
- CFRunloopRef
- CFRunloopModeRef
- CFRunloopSourceRef
- CFRunloopTimerRef
- CFRunloopObserverRef
看一下源码,了解一下他们之间的关系,只显示主要的
struct __CFRunLoop {
CFRunLoopModeRef _currentMode;//当前模式
CFMutableSetRef _modes;//模式的集合,里面装的是CFRunloopModeRef对象
};
struct __CFRunLoopMode {
CFMutableSetRef _sources0;//里面装的是CFRunloopSourceRef对象
CFMutableSetRef _sources1;//里面装的是CFRunloopSourceRef对象
CFMutableArrayRef _observers;//里面装的是CFRunloopObserverRef对象
CFMutableArrayRef _timers;//里面装的是CFRunloopTimerRef对象
};
用图表示

一个RunLoop有若干个model,一个model里有sources0、sources1、observer、timers
六、CFRunloopModeRef
1、CFRunloopModeRef代表Runloop的运行模式
2、一个RunLoop有若干个model,一个model里有sources0、sources1、observer、timers
3、RunLoop启动时只能选择其中的一个model,作为currentMode
4、如果需要切换mode,只能退出当前loop,再重新选择一个mode进入,这样做的好处:不同组的sources0、sources1、observer、timers能分割开来,互不影响
5、如果mode里没有任何sources0、sources1、observer、timers,RunLoop会立马退出
6.1 Mode的五种模式
常用的几个:
1、kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
2、UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
5、kCFRunLoopCommonModes: 这是一个占位用的Mode,并不是一种真正的Mode,默认包括Default和Tracking模式。
3、UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
4、GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
6.2 source0
Source0是用来处理触摸事件、Perfrom Selectors
代码看一下

6.3 source1
Source1是用来处理基于Port的 线程之间的通信、系统事件捕捉
6.4 timers
timers 处理 定时器 NSTimer
6.5 observer
监听器,监听runLoop的状态、UI刷新、AutoreleasePool
七、Runloop运行逻辑

八、应用
1、解决NSTimer在滑动时停止工作的问题
代码如下
static int count = 0;
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%d",++count);
}];
运行期间我滑动了,tableView,打印结果如下

看到中间隔了10秒,才打印,为什么呢?上述方法是直接添加到DefaultMode下工作,滑动的时候是在TrackingMode下进行的,解决如下
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
repeats:YES
block:^(NSTimer * _Nonnull timer) {
NSLog(@"%d",++count);
}];
//timer 在CommonModes下工作
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
九、相关问题
1、讲讲Runloop,项目中有用到吗?
2、Runloop内部实现逻辑?
3、Runloop和线程的关系?
4、程序中添加每3秒相应一次的NSTimer,当拖动tableView时,timer可能无法响应要怎么解决?
5、Runloop是怎么响应用户操作的,具体流程是什么样的?
6、Runloop的几种状态?
7、Runloop的mode作用是什么?
网友评论