iOS 底层 - runloop的源码分析
2020-04-05 本文已影响0人
水中的蓝天
本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !
在开始之前需要对:iOS 底层 - runloop内部的数据结构 有一个简单认识会好一些!
简化后的源码解读 - -CFRunLoop.c
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
//执行入口
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
//RunLoop没有停止且没有结束就一直执行CFRunLoopRunSpecific函数
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
//通知Observer:即将进入loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//核心执行逻辑
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//通知Observer:即将退出loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
//通知Observers 即将处理Timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//通知Observers 即将处理Sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
//开始处理Sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {//有用到Blocks,就处理
__CFRunLoopDoBlocks(rl, rlm);
}
//判断有没有Sources1,有没有MachPort线程通信
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
//有就跳转到handle_msg函数处理
goto handle_msg;
}
//通知Observers 即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
//开始休眠
__CFRunLoopSetSleeping(rl);
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
//等待别的消息来唤醒当前线程,不再往下执行代码了 !一直到有人唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
//结束休眠
__CFRunLoopUnsetSleeping(rl);
//通知Observers 结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
//handle_msg函数处理
handle_msg:
if (被Timer唤醒) {
//处理Timers
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
}else if (被GCD唤醒) {
//处理GCD相关的事件
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else {//被Sources1唤醒
//处理Sources1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
}
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
//处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
//设置返回值
if (sourceHandledThisLoop && stopAfterHandle) {
//处理源
retVal = kCFRunLoopRunHandledSource; 4
} else if (timeout_context->termTSR < mach_absolute_time()) {
//超时
retVal = kCFRunLoopRunTimedOut; 3
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
//暂停
retVal = kCFRunLoopRunStopped; 2
} else if (rlm->_stopped) {
rlm->_stopped = false;
//暂停
retVal = kCFRunLoopRunStopped; 2
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
//完成
retVal = kCFRunLoopRunFinished; 1
}
voucher_mach_msg_revert(voucherState);
os_release(voucherCopy);
} while (0 == retVal);
return retVal;
}
runloop 调用细节
- 关于:__CFRunLoopDoObservers
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */
//通知Observer处理事件的核心函数调用
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);
}
- 关于 __CFRunLoopDoTimers
// mode and rl are locked on entry and exit
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */
//真正处理定时器逻辑的函数调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
return timerHandled;
}
- 关于 __CFRunLoopDoSources0
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) { /* DOES CALLOUT */
//真正处理Sources0的函数调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
return sourceHandled;
}
- 关于 __CFRunLoopDoBlocks
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
//真正处理__CFRunLoopDoBlocks的函数调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
return did;
}
- 关于 GCD
//真正处理GCD-->dispatch_queue的函数调用
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
GCD
的执行逻辑一般是不依赖于RunLoop,只有少数情况会交给RunLoop处理;比如:异步子线程回到主线程刷新UI
、dispatch_after();这样的操作才会交给RunLoop处理
// 回到主线程去刷新UI界面
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"嘿嘿嘿😝");
});
- 关于
__CFRunLoopServiceMachPort
线程休眠
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout, voucher_mach_msg_state_t *voucherState, voucher_t *voucherCopy) {
//真正处理__CFRunLoopServiceMachPort的函数调用
ret = mach_msg(msg, MACH_RCV_MSG|(voucherState ? MACH_RCV_VOUCHER : 0)|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, port, timeout, MACH_PORT_NULL);
return false;
}
__CFRunLoopServiceMachPort
: 让当前线程进入休眠状态,不做任何事情;CPU也不需要再给当前线程分配任何资源、代码也就停在该函数的位置不在向下执行,一直到被唤醒后才会继续执行下面的代码,这个阻塞线程是真正的阻塞;不是在那里搞一个死循环,这一点是需要区分开 !
如何做到的呢 ?
在源码中可以看到是调用内核层面的API-->mach_msg()函数实现;
操作系统提供的API按层级换分会有很多种:
- 应用层面API: 这个层面的API是给开发者编写程序使用的,比如:编写UI界面、发送通知、发网络请求等
- 内核层面API:这种层面的API通常情况下是不会开放给开发者调用的,这很危险;
图解:
