1.执行流程图
2.源码
由Runloop(一)可知,首先调用了__CFRunloopRunSpecific --> __CFRunloopRun
在源码中搜索:CFRunloop.c文件,文件中搜索__CFRunloopRunSpecific
源码间接版伪代码如下:
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
//通知Observers: 进入Loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 具体要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers:退出Loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
//Macos ,嵌入式,mini嵌入式(平板就是嵌入式)
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#endif
int32_t retVal = 0;
do {
//通知observers:即将处理timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//通知observers:即将处理source
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理blocks
__CFRunLoopDoBlocks(rl, rlm);
//处理source0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
//处理blocks
__CFRunLoopDoBlocks(rl, rlm);
}
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
//端口相关 判断有无source1
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
//如果哟source1 跳转到 handle_msg
goto handle_msg;
}
}
didDispatchPortLastTime = false;
//通知Observers:即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
// do not do any user callouts after this point (after notifying of sleeping)
// Must push the local-to-this-activation ports in on every loop
// iteration, as this mode could be run re-entrantly and we don't
// want these ports to get serviced.
//等待别的消息来唤醒当前线程
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
__CFRunLoopUnsetSleeping(rl);
通知Observers:结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (被timer唤醒) {
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
// handle nothing
}
else if (被GCD唤醒) {
//处理GCD
__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);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
- 处理block:
CFRunLoopPerformBlock(<#CFRunLoopRef rl#>, <#CFTypeRef mode#>, <#^(void)block#>);
3.调用的细节
Runloop很多API,最终会调一些很长的函数。比如__CFRunLoopDoObservers最终会调如下图:
如图:
- GCD不依赖Runloop,GCD很多操作自己处理,只不过有些情况会交给Runloop处理。如下:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 处理一些子线程的逻辑
// 回到主线程去刷新UI界面
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"11111111111");
});
Runloop休眠的细节
源码中Runloop休眠的实现__CFRunLoopServiceMachPort,调的是内核API: Mach_msg