Runloop的运行逻辑

CFRunloopObserver的创建及监听

监听CFRunloopModeRef的切换
在主线程默认开启runloop,模式为kCFRunloopDefaultMode,当屏幕滚动时,会退出当前mode,重新选择UITrackingMode进入,当停止滚动时,就会结束当前mode,重新回到defaultMode

Runloop的运行逻辑流程图

Runloop的运行逻辑的底层代码实现
1.先调用CFRunLoopRunSpecific方法
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
//1.通知observer,进入loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//2.runloop具体要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//3.通知observer,退出loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
2.再调用__CFRunLoopRun方法(内部有一个do while 循环)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
//通知observer,即将处理timer
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//通知observer,即将处理sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理blocks
__CFRunLoopDoBlocks(rl, rlm);
//处理source0
if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle);) {
//处理blocks
__CFRunLoopDoBlocks(rl, rlm);
}
//判断有无source1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
//如果有source1,就跳转到handle_msg
goto handle_msg;
}
//如果没有source1,则通知observer,即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
//执行休眠操作
__CFRunLoopSetSleeping(rl);
//等待别的消息来唤醒当前线程
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
//结束休眠
__CFRunLoopUnsetSleeping(rl);
//通知observer,结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (被timer唤醒) {
//处理timers
__CFArmNextTimerInMode(rlm, rl);
}
else if (livePort == dispatchPort) { //被GCD唤醒
// 处理GCD相关事件 : 例如主线程更新UI
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { //被source1唤醒
//处理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop
}
//处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
//设置返回值
if (sourceHandledThisLoop && stopAfterHandle) {
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)) {
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}
Runloop实现休眠的原理
__CFRunLoopServiceMachPort内部调用mach_msg()函数,此时会调用内核层面的API(相对于应用层面的API),执行应用层面的API时,是用户态,内核API则是内核态。在用户态调用mach_msg时,会自动转到内核态的mach_msg,达到真正休眠的目的,没有消息,就让线程休息,有消息就唤醒线程,切换到用户态,处理消息。


网友评论