深刻浅出 RunLoop(一):初识
深刻浅出 RunLoop(二):数据结构
深刻浅出 RunLoop(三):事件循环机制
深刻浅出 RunLoop(四):RunLoop 与线程
深刻浅出 RunLoop(五):RunLoop 与 NSTimer
iOS - 聊聊 autorelease 和 @autoreleasepool:RunLoop 与 @autoreleasepoolmarkdown
前面咱们介绍了RunLoop
的基本概念以及相关数据结构,这篇咱们来说解一下RunLoop
究竟是怎么工做的。数据结构
首先咱们来看一下主线程的RunLoop
的启动过程。
前面咱们说过,咱们的 iOS 程序能保持持续运行的缘由就是在main()
函数中调用了UIApplicationMain
函数,这个函数内部会启动主线程的RunLoop
。
打断点,经过 LLDB 指令bt
查看函数调用栈以下: app
UIApplicationMain
函数中调用了 Core Foundation 框架下的
CFRunLoopRunSpecific
函数。
查看源码中该函数的实现,以下:框架
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished; __CFRunLoopLock(rl); // 根据 modeName 找到本次运行的 mode CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false); // 若是没找到 || mode 中没有注册任何事件,则就此中止,不进入循环 if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) { Boolean did = false; if (currentMode) __CFRunLoopModeUnlock(currentMode); __CFRunLoopUnlock(rl); return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished; } volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl); CFRunLoopModeRef previousMode = rl->_currentMode; rl->_currentMode = currentMode; int32_t result = kCFRunLoopRunFinished; // 通知 Observers:即将进入 RunLoop if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); // RunLoop 具体要作的事情 result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode); // 通知 Observers:即将退出 RunLoop if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); __CFRunLoopModeUnlock(currentMode); __CFRunLoopPopPerRunData(rl, previousPerRun); rl->_currentMode = previousMode; __CFRunLoopUnlock(rl); return result; } 复制代码
删掉不重要的细节:async
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ // 通知 Observers:即将进入 RunLoop __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); // RunLoop 具体要作的事情 result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode); // 通知 Observers:即将退出 RunLoop __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); return result; } 复制代码
从函数调用栈,以及CFRunLoopRunSpecific
函数的实现中能够得知,RunLoop
事件循环的实现机制体如今__CFRunLoopRun
函数中。 函数
因为该函数实现较复杂,如下为删掉细节的精简版本,想探究具体的能够查看 Core Foundation 源码。oop
/** * __CFRunLoopRun * * @param rl 运行的 RunLoop 对象 * @param rlm 运行的 mode * @param seconds loop 超时时间 * @param stopAfterHandle true: RunLoop 处理完事件就退出 false:一直运行直到超时或者被手动终止 * @param previousMode 上一次运行的 mode * * @return 返回 4 种状态 */ 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 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; } // 通知 Observers:即将进入休眠 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); __CFRunLoopSetSleeping(rl); // ⚠️休眠,等待消息来唤醒线程 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy); __CFRunLoopUnsetSleeping(rl); // 通知 Observers:刚从休眠中唤醒 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); handle_msg: if (被 Timer 唤醒) { // 处理 Timer __CFRunLoopDoTimers(rl, rlm, mach_absolute_time()) } 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); /* 设置返回值 */ // 进入 loop 时参数为处理完事件就返回 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; // mode 中没有任何的 Source0/Source1/Timer/Observer } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { retVal = kCFRunLoopRunFinished; } } while (0 == retVal); return retVal; } 复制代码
从该函数实现中能够得知RunLoop
主要就作如下几件事情:post
Observers
接下来要作什么Blocks
Sources0
Sources1
Timers
dispatch_async(dispatch_get_main_queue(), ^{ });
在__CFRunLoopRun
函数中,会调用__CFRunLoopServiceMachPort
函数,该函数中调用了mach_msg()
函数来转移当前线程的控制权给内核态/用户态。this
mach_msg()
从用户态切换到内核态,等待消息;mach_msg()
回到用户态处理消息。这就是RunLoop
休眠的实现原理,也是RunLoop
与简单的do...while
循环区别:spa
RunLoop
:休眠的时候,当前线程不会作任何事,CPU 不会再分配资源;do...while
循环:当前线程并无休息,一直占用 CPU 资源。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) { Boolean originalBuffer = true; kern_return_t ret = KERN_SUCCESS; for (;;) { /* In that sleep of death what nightmares may come ... */ mach_msg_header_t *msg = (mach_msg_header_t *)*buffer; msg->msgh_bits = 0; msg->msgh_local_port = port; msg->msgh_remote_port = MACH_PORT_NULL; msg->msgh_size = buffer_size; msg->msgh_id = 0; if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); } // ⚠️⚠️⚠️ 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); // Take care of all voucher-related work right after mach_msg. // If we don't release the previous voucher we're going to leak it. voucher_mach_msg_revert(*voucherState); // Someone will be responsible for calling voucher_mach_msg_revert. This call makes the received voucher the current one. *voucherState = voucher_mach_msg_adopt(msg); if (voucherCopy) { if (*voucherState != VOUCHER_MACH_MSG_STATE_UNCHANGED) { // Caller requested a copy of the voucher at this point. By doing this right next to mach_msg we make sure that no voucher has been set in between the return of mach_msg and the use of the voucher copy. // CFMachPortBoost uses the voucher to drop importance explicitly. However, we want to make sure we only drop importance for a new voucher (not unchanged), so we only set the TSD when the voucher is not state_unchanged. *voucherCopy = voucher_copy(); } else { *voucherCopy = NULL; } } CFRUNLOOP_WAKEUP(ret); if (MACH_MSG_SUCCESS == ret) { *livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL; return true; } if (MACH_RCV_TIMED_OUT == ret) { if (!originalBuffer) free(msg); *buffer = NULL; *livePort = MACH_PORT_NULL; return false; } if (MACH_RCV_TOO_LARGE != ret) break; buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE); if (originalBuffer) *buffer = NULL; originalBuffer = false; *buffer = realloc(*buffer, buffer_size); } HALT; return false; } 复制代码