这篇文章主要总结下runloop的主要组成及实现流程,以及衍生出来的一些相关问题:
1.组成
2.主要方法(实现流程)
3.线程保活需要添加item
4.GCD和NSTimer的准确性
1.组成
CFRunLoop类,
CFRunLoopMode类,
CFRunLoopSource类,
CFRunLoopTimer类,
CFRunLoopObserver类
//一个runloop中包含
struct __CFRunLoop {
pthread_t _pthread; //对应的线程,runloop和线程是一一对应的
CFMutableSetRef _commonModes; //commonMode是针对kCFRunLoopCommonModes,是_modes的子集
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode; //当前的mode
CFMutableSetRef _modes; //runloop中所有的mode
};
//mode
struct __CFRunLoopMode {
CFMutableSetRef _sources0; //系统事件
CFMutableSetRef _sources1; //线程通信
CFMutableArrayRef _observers; //监听者
CFMutableArrayRef _timers; //定时器
};
mode类型有
UITrackingRunLoopMode //处理滚动
kCFRunLoopDefaultMode //默认模式
UIInitializationRunLoopMode //启动程序后的过渡mode,启动完成后就不再使用
GSEventReceiveRunLoopMode //Graphic相关事件的mode,通常用不到
kCFRunLoopCommonModes //监听commonModes中mode,比如mainRunloop中commonModes包含UITrackingRunLoopMode,kCFRunLoopDefaultMode,所以timer设置mode = kCFRunLoopCommonModes,就可以同时监听这两种mode下的活动
2.实现流程
其中runloop主要方法是
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
//判断mode是否存在,mode中是否有需要执行的东西,通过这里可以看出如果需要让一个线程保活,这里需要开启runloop之外,还需要有mode和任务(source,timer,observer...)
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
//通知监听者即将进入runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//执行runloop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//通知监听者已经退出runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
//就是一个do-while循环
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
...(休眠)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
retVal = ...(来决定是否退出循环)
} while (0 == retVal);
return retVal;
}
这个方法run函数中,最终用来执行这些source,timer..的就是下面图中,右边黑框中的函数

WechatIMG19.jpeg
其实我们的函数调用大部分都是始于runLoop,比如ViewController中的viewDidLoad,这些方法(方法链,我指的是整个函数调用栈,什么时候开始的第一个函数调用)什么时候调用,都是runloop来控制的,我们在viewDidLoad中打断点发现

WechatIMG18.jpeg
3.线程保活
-(void)threadAlive {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (1) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
[thread start];
}
- [[NSRunLoop currentRunLoop] run] 是无法停止的,所以如果不是整个app都需要保活的话要使用上述方案;
- 需要添加任务到mode中,否则无法开启runloop循环, 上述CFRunLoopRunSpecific函数中有明确的代码;