RunLoop的运行流程图
CFRunLoop底层实现流程图.pngRunLoop从入口函数开始都做了什么?
1.从CFRunLoopRun
入口函数可以看到,它用do...while
不断执行CFRunLoopRunSpecific
2.CFRunLoopRunSpecific
中传入当前的Loop
、默认Model
、second
值1.0e10、BOOL returnAfterSourceHandled
。
CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
3.根据传入的modeName
,通过__CFRunLoopFindMode
获得当前的CFRunLoopModeRef currentMode
。
- 如果没找到或者
mode
中没有注册任何事件,则就此停止,不进入循环
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
- 取上一次运行的
mode
,将当前runloop
的mode
替换成 第三步找到的mode
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
6.初始化一个result
为kCFRunLoopRunFinished
,先通知Observers
: RunLoop
即将进入 loop
,然后执行__CFRunLoopRun
,结果为result
赋值,最后在通知 Observers
: RunLoop
即将退出。
int32_t result = kCFRunLoopRunFinished;
if (currentMode->_observerMask & kCFRunLoopEntry ){
/// 1. 通知 Observers: RunLoop 即将进入 loop。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
}
result = __CFRunLoopRun(rl, currentMode, seconds,
returnAfterSourceHandled, previousMode);
if (currentMode->_observerMask & kCFRunLoopExit ){
/// 10. 通知 Observers: RunLoop 即将退出。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
}
7.当RunLoop
退出后,进行一系列操作,Unlock
当前通过第三步获取到的mode
,并将当前RunLoop
的_currentMode
设置为先前保存下来的previousMode
,返回result
。
__CFRunLoopModeUnlock(currentMode);
__CFRunLoopPopPerRunData(rl, previousPerRun);
rl->_currentMode = previousMode;
__CFRunLoopUnlock(rl);
return result;
核心方法__CFRunLoopRun
里做了什么?
image.png
1.通知observer
:即将进入RunLoop
if (currentMode->_observerMask & kCFRunLoopEntry ){
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
}
__CFRunLoopRun
内执行的就是上图虚线框内的内容
以下内容,按图中顺序描述。
2.通知observer
:将要处理Timer
if (rlm->_observerMask & kCFRunLoopBeforeTimers) {
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
}
3.通知observer
:将要处理source0
if (rlm->_observerMask & kCFRunLoopBeforeSources) {
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
}
4.处理source0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
5.如果有source1
,跳转到第九步,source1
包含一个 mach_port
和一个回调(函数指针),
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
//如果接收到了消息的话,前往第9步开始处理msg
goto handle_msg;
}
6.通知observer
:runloop
即将休眠。判断如果没有sources0
事件处理并且没有超时,poll
为false
;如果有sources0
事件处理 或者 超时,poll
都为true
。如果poll
为false
且rlm->_observerMask
为kCFRunLoopBeforeWaiting
,就执行__CFRunLoopDoObservers
。
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
7.设置runloop
为休眠状态__CFRunLoopSetSleeping(rl);
后执行一个内循环do...while
,进入循环后线程进入休眠,直到收到新消息才跳出该循环(被唤醒),继续执行run loop。
这里有个纠结的点:为什么设置的是runloop
为休眠状态,休眠的却是线程?
runloop与线程是一一对应的,一个runloop对应一个核心的线程,为什么说是核心的,是因为runloop是可以嵌套的,但是核心的只能有一个,他们的关系保存在一个全局的字典里。
runloop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。
上面这段摘取自这位大佬
在等待唤醒的过程中,如果USE_DISPATCH_SOURCE_FOR_TIMERS
将会执行一个内循环
do {
//从缓冲区读取消息
msg = (mach_msg_header_t *)msg_buffer;
//7.调用__CFRunLoopServiceMachPort, 监听waitSet所指定的mach port端口集合, 如果没有port message,进入 mach_msg_trap, RunLoop休眠,直到收到port message或者超时
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
//收到消息之后,livePort的值为msg->msgh_local_port,然后livePort!=modeQueuePort,所以会走else
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
// Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
//耗尽内部队列,如果有一个回调的block 设置了timerFired,跳出循环,并且为这个timer服务(翻译过来是这样,但是不太懂)
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
if (rlm->_timerFired) {
// Leave livePort as the queue port, and service timers below
rlm->_timerFired = false;
break;//退出循环,即将去执行唤醒操作
} else {
//如果msg不为空,并且msg跟msg_buffer不匹配,就释放这条msg,当做什么事都没发生
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
// Go ahead and leave the inner loop.
// 继续往下走,离开内循环
break;//退出循环,即将去执行唤醒操作
}
} while (1);
这里有一点让我很纠结,就是这些各种各样的Port
都是啥,livePort
如何跟modeQueuePort
相等。
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort)
这个判断又要如何成立。
于是我全局搜索 modeQueuePort
,发现有赋值的地方只有一个且需要USE_DISPATCH_SOURCE_FOR_TIMERS
。
modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
然后继续搜索livePort
,在__CFRunLoopServiceMachPort
的实现方法中找到了,当内核成功收到消息时,livePort
会被赋值为msg->msgh_local_port
if (MACH_MSG_SUCCESS == ret) {//成功收到消息
*livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL;
return true;
}
所以,只要成功收到消息,livePort
就不等于modeQueuePort
,那么就会跳出循环。
但是继续往下走又会看到一串代码,判断规则跟上面一模一样,但是这个方法里却唤醒了RunLoop
。
#if USE_DISPATCH_SOURCE_FOR_TIMERS
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
}
我勒个去,什么玩意,上面好不容易解读出来,到这里就冲突了,一度怀疑人生。吓得我赶紧找资料去。
终于,我在这位大佬的博客里找到了关于这段的解释。
这里不是从runLoop休眠后唤醒到这里的,而是在runLoop10步中的第五步跳转过来的,是处理计时器事件
这下子总算是把内循环理得差不多清楚了
8.通知observer
:线程刚被唤醒。 休眠结束。
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) {
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
}
9:处理唤醒时收到的消息,然后回到第二步。
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
if (MACH_PORT_NULL == livePort) {
CFRUNLOOP_WAKEUP_FOR_NOTHING();
// handle nothing
} else if (livePort == rl->_wakeUpPort) {
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
// do nothing on Mac OS
}
#if USE_DISPATCH_SOURCE_FOR_TIMERS
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
//这里不是从runLoop休眠后唤醒到这里的
//而是在runLoop10步中的第五步跳转过来的,是处理计时器事件
//www.jianshu.com/p/ec629063390f
CFRUNLOOP_WAKEUP_FOR_TIMER();
/// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
/// 9.2 如果有dispatch到main_queue的block,执行block
else if (livePort == dispatchPort) {
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
didDispatchPortLastTime = true;
} else {
/// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
CFRUNLOOP_WAKEUP_FOR_SOURCE();
// Despite the name, this works for windows handles as well
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
mach_msg_header_t *reply = NULL;
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
if (NULL != reply) {
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
}
}
/// 执行跟上面一个样的方法,应该就是回到第二步
__CFRunLoopDoBlocks(rl, rlm);
10.通知observer
:即将退出RunLoop
。为retVal
赋值,如果retVal != 0
,将会退出循环,并返回retVal
。
if (sourceHandledThisLoop && stopAfterHandle) {
/// 进入loop时参数说处理完事件就返回。
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
/// 超出传入参数标记的超时时间了
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
/// 被外部调用者强制停止了
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
/// 自动停止了
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
/// source/timer/observer一个都没有了
retVal = kCFRunLoopRunFinished;
}
/// 如果没超时,mode里不为空,loop也没被停止,那继续loop。
回到外层方法__CFRunLoopRun
调用并且为result
赋值。
if (currentMode->_observerMask & kCFRunLoopExit ){
/// 10. 通知 Observers: RunLoop 即将退出。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
}
return result;
当result == kCFRunLoopRunStopped && result == kCFRunLoopRunFinished
结束CFRunLoopRun
网友评论