一、什么是RunLoop
先来看下RunLoop
中CFRunLoopRun
函数的实现:
void CFRunLoopRun(void){
int32_t result;
do{
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
} while(kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result)
}
开启RunLoop
之后会调用CFRunLoopRun
函数,该函数使用do{}while()
循环不断处理CFRunLoopRunSpecific
,直到RunLoop
结束或者需要停止才退出。
RunLoop
接收到消息时(用户输入、程序执行)会处理消息,空闲时休眠,是一种Event loop
的模型。RunLoop
开启时,一直处于接受消息-等待-处理
的循环中。这里我们着重介绍下CFRunLoop
,至于NSRunLoop
,其实就是对CFRunLoop
的一层封装。
二、RunLoopMode
RunLoopMode
就是RunLoop
的相关模式,其定义如下:
struct __CFRunLoopMode{
CFRuntimeBase _base;
pthread_mutex_t _lock; // 锁
CFStringRef _name; // 模式名字
Boolean _stopped; // 是否停止
char _padding[3];
CFMutableSetRef _sources0; // source0
CFMutableSetRef _sources1; // source1
CFMutableArrayRef _observers; // observers
CFMutableArrayRef _timers; // timers
// ... other
};
常用的Mode有以下几个:
Mode | 说明 |
---|---|
kCFRunLoopDefaultMode | 默认运行方式 |
UITrackingRunLoopMode | 使用这个Mode去跟踪来自用户交互的事件 |
GSEventReceiveRunLoopMode | 用来接受系统事件,内部的Run Loop Mode |
kCFRunLoopCommomModes | 这是一个伪模式,其为一组run loop mode的集合 |
NSConnectionReplyMode | 使用这个Mode去监听NSConnection对象的状态,我们很少需要自己使用这个Mode |
三、RunLoopSource
struct __CFRunLoopSource{
CFRuntimeBase _base;
uint32_t bits;
pthread_mutex_t _lock;
CFIndex _order;
CFMutableBagRef _runLoops;
union{
CFRunLoopSourceContext version0;
CFRunLoopSourceContext1 version1;
} _context;
};
source
有两个版本:version0
和version1
,分别对应CFRunLoopMode
的source0
和source1
。
source0
需要进行标记为可操作,然后再唤醒(wakeup)runloop进行处理。
source1
是基于mach_port
的,用于跟其它线程互相发送消息,通信。
四、RunLoopTimer
CFRunLoopTimer
是定时器,包含时间和回调函数指针,时间点到时,RunLoop
会被唤醒并执行该定时器。
五、RunLoopObserver
struct __CFRunLoopObserver{
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities;
CFIndex _order;
CFRunLoopObserverCallBack _callout;
CFRunLoopObserverContext _context;
};
CFRunLoopObserverRef
是一个观察者,当RunLoop
的状态改变时,会通过_callout
回调。状态有以下几种:
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工作流程
首先来看下简化版的CFRunLoopRunSpecific
函数:
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled){
// 即将进入Loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
result = __CFRunLoop(rl, currentMode, seconds, returnAfterSourceHandled, previousMode){
// 即将处理 Timer
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopBeforeTimers);
// 即将处理 Source
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopBeforeSources );
// source0
__CFRunLoopDoBlocks(rl, rlm);
BOOLean sourceHandledThisStop = __CFRunLoopDoSource0(rl, rlm, stopAfterHandle);
if(sourceHandledThisStop){
//如果有source0需要处理的话,sourceHandledThisStop为true
__CFRunLoopDoBlocks(rl, rlm);
}
if(MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime){
// 检查livePort是否有待处理的消息,如果有则goto handle_msg,否则进入休眠等待唤醒
msg = (mach_msg_header_t *)msg_buffer;
if(__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)){
goto handle_msg;
}
}
// 即将进入休眠
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopBeforeWaiting);
// 监听端口,等待消息唤醒
do{
msg = (mach_msg_header_t *)msg_buffer;
// TIMEOUT_INFINITY无限等待
__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
if(modeQueuePort != MACH_PROT_NULL && modeQueuePort == modeQueuePort){
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
if(rlm->_timerFired){
// 处理timer
rlm->_timerFired = false;
break;
}else{
// free msg
}
}else{
// 处理其它消息
break;
}
} while(1);
// 刚从休眠中唤醒
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopAfterWaiting );
handle_msg:
// 根据livePort处理timer, source1等
if(MACH_PORT_NULL == livePort){
}else if(livePort == rl->_wakeUpPort){
// wakeUp
}else if(rlm->_timerPort == livePort){
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
}else if(livePort == dispatchPort){
__CFRUNLOOP_IS_SERVICINT_THE_MAIN_DISPATCH_QUEUE__(msg);
}else{
// source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
}
// 改变状态,如果retVal不为0,则代表需要结束RunLoop了
if(sourceHandledThisLoop && stopAfterHandle){
retVal = kCFRunLoopRunHandledSource;
}else if(timeout_context->termTSR < mach_absolute_time()){
retVal = kCFRunLoopRunTimedOut;
}else if(_CFRunLoopIsStopped(rl)){
retVal = kCFRunLoopRunStopped;
}else if(rlm->_stopped){
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
}else if(_CFRunLoopModeIsEmpty(rl, rlm, previousMode)){
retVal = kCFRunLoopRunFinished;
}
} while(0 == retVal)
// 7. 即将退出Loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit );
return result;
}
RunLoop
流程主要如下:
- 即将进入Loop
- 将要处理Timer
- 将要处理Source0
- 处理Source0
- 如果有Source1或Timer待处理,则跳转到第步
- 通知即将休眠
- 进入休眠,等待唤醒
- 通知被唤醒
- 处理Source1和Timer
- 如果RunLoop仍需处理其它消息,则跳转到2
- 即将推出Loop