开头
这是一篇探究runloop内部原理实现的文章,不涉及具体使用。
内部实现
下面部分会以CFRunLoop说明,因为NSRunloop只是对CFRunLoop的封装
do-while 循环
源码
void CFRunLoopRun(void) { /* DOES CALLOUT */ int32_t result; do { result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); CHECK_FOR_FORK(); } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result); }
结论
可以看到,runloop在run起来后其实是用一个do-while循环实现的,不同的是,runloop可以做到不需要处理事务的时候就sleep,需要的时候就work。其作用总结就是:
- 保持程序的持续运行
- 处理App中各种事件(触摸、定时器、performSelector)
- 节省CPU资源、提供程序的性能:该做事做事,该休息休息
与线程的关系
源码
##### mainRunloop CFRunLoopRef mainRunloop = CFRunLoopGetMain(); ##### CFRunLoopGetMain()函数内部实现 CFRunLoopRef CFRunLoopGetMain(void) { CHECK_FOR_FORK(); static CFRunLoopRef __main = NULL; // no retain needed //可以看到传了一个主线程参数进入 if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed return __main; } ##### _CFRunLoopGet0()函数内部实现 CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) { if (pthread_equal(t, kNilPthreadT)) { t = pthread_main_thread_np(); } __CFSpinLock(&loopsLock); if (!__CFRunLoops) { __CFSpinUnlock(&loopsLock); //建立全局的字典用来存储 线程和runloop的关系 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); //通过__CFRunLoopCreate()函数创建runloop实例对象 CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); //将mainLoop 和 mainThreadb存入字典 CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) { CFRelease(dict); } CFRelease(mainLoop); __CFSpinLock(&loopsLock); } //主线程的loop在上面就已经创建了,所以如果获取不到,那肯定是子线程的loop CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); __CFSpinUnlock(&loopsLock); //没有loop,那就创建子线程的loop对象 if (!loop) { CFRunLoopRef newLoop = __CFRunLoopCreate(t); __CFSpinLock(&loopsLock); loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); if (!loop) { //子线程和其runloop也会被存在这个全局的字典中 CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop); loop = newLoop; } // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it __CFSpinUnlock(&loopsLock); CFRelease(newLoop); } if (pthread_equal(t, pthread_self())) { _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL); if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) { _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop); } } //返回runloop return loop; }
结论
- 系统会创建一个全局的可变字典CFMutableDictionaryRef dict,储存线程和runloop;
- 主线程的runloop是默认创建的
- 子线程的runloop默认不启动,需要主动获取
内部的mode-item关系
源码
#### __CFRunLoop对象内部结构 struct __CFRunLoop { CFRuntimeBase _base; pthread_mutex_t _lock; /* locked for accessing mode list */ __CFPort _wakeUpPort; // used for CFRunLoopWakeUp Boolean _unused; volatile _per_run_data *_perRunData; // reset for runs of the run loop //关联的线程 pthread_t _pthread; uint32_t _winthread; //有好多的Modes、Items CFMutableSetRef _commonModes; CFMutableSetRef _commonModeItems; //但是只有一个currentMode CFRunLoopModeRef _currentMode; CFMutableSetRef _modes; struct _block_item *_blocks_head; struct _block_item *_blocks_tail; CFTypeRef _counterpart; }; struct __CFRunLoopMode { CFRuntimeBase _base; pthread_mutex_t _lock; /* must have the run loop locked before locking this */ CFStringRef _name; Boolean _stopped; char _padding[3]; //有好多的事件源 _sources0、_sources1、_observers、_timers CFMutableSetRef _sources0; CFMutableSetRef _sources1; CFMutableArrayRef _observers; CFMutableArrayRef _timers; CFMutableDictionaryRef _portToV1SourceMap; __CFPortSet _portSet; CFIndex _observerMask; #if USE_DISPATCH_SOURCE_FOR_TIMERS dispatch_source_t _timerSource; dispatch_queue_t _queue; Boolean _timerFired; // set to true by the source when a timer has fired Boolean _dispatchTimerArmed; #endif #if USE_MK_TIMER_TOO mach_port_t _timerPort; Boolean _mkTimerArmed; #endif #if DEPLOYMENT_TARGET_WINDOWS DWORD _msgQMask; void (*_msgPump)(void); #endif uint64_t _timerSoftDeadline; /* TSR */ uint64_t _timerHardDeadline; /* TSR */ };
结论
- __CFRunLoop对象内部包含多个modes
- 而每个mode中又包含了多个items
- items就是事件源_sources0、_sources1、_observers、_timers...
- 但是__CFRunLoop对象同时只能持有一种mode:_currentMode
runloop对象结构图
Runloop的item
runloop有六大事务item,这些item都是由runloop调用的。先看代码
- (void)timerItem{ //__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { NSLog(@"test timer"); }]; }
等程序运行起来在断点看内存堆栈调用
(lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2 * frame #0: 0x000000010b59a240 01-Runloop初探`__28-[ViewController sourceDemo]_block_invoke(.block_descriptor=0x0000600000658318, timer=0x0000600000f58010) at ViewController.m:26 frame #1: 0x000000010b987413 Foundation`__NSFireTimer + 72 frame #2: 0x000000010bfb3b94 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20 frame #3: 0x000000010bfb3882 CoreFoundation`__CFRunLoopDoTimer + 1026 frame #4: 0x000000010bfb2eda CoreFoundation`__CFRunLoopDoTimers + 266 frame #5: 0x000000010bfadc4e CoreFoundation`__CFRunLoopRun + 2238 frame #6: 0x000000010bfad066 CoreFoundation`CFRunLoopRunSpecific + 438 frame #7: 0x00000001154c6bb0 GraphicsServices`GSEventRunModal + 65 frame #8: 0x000000010f2e0d4d UIKitCore`UIApplicationMain + 1621 frame #9: 0x000000010b59a471 01-Runloop初探`main(argc=1, argv=0x00007ffee4664cf0) at main.m:21:16 frame #10: 0x000000010d90fc25 libdyld.dylib`start + 1
可以看到在调用timer回调之前,调用了函数__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__,我们再去看源码
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) { if (func) { func(timer, info); } asm __volatile__(""); // thwart tail-call optimization }
可以看到确实是先调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__,再调用timer回调函数func(timer,info)。这里只是timer-item这一种,下面是iOS中所有的item调用情况:
- block: __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
- timer:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
- 响应source0:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__触摸事件
- 响应source1:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__系统端口事件
- GCD主队列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
- observer源:__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
事务是如何被调用的
还是以timer 调用为例子说明
NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { NSLog(@"fire in home -- %@",[[NSRunLoop currentRunLoop] currentMode]); }]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
存item
先看- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode内部做了什么
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) { CFTypeRef item = (CFTypeRef)value; CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]); CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]); if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) { CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName); } else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) { CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName); } else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) { CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName); } } #### CFRunLoopAddTimer() void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return; if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return; __CFRunLoopLock(rl); //判断当前loop的mode类型 if (modeName == kCFRunLoopCommonModes) { CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; if (NULL == rl->_commonModeItems) { rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); } //将timers添加到当前rl->_commonModeItems中 CFSetAddValue(rl->_commonModeItems, rlt); //判断set是否存在 if (NULL != set) { CFTypeRef context[2] = {rl, rlt}; /* add new item to all common-modes */ //将这个timer 提交到当前loop的上下文中,方便后面使用去取 CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); CFRelease(set); } } else {//其他类型也差不多,这里就不再细说 CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true); if (NULL != rlm) { if (NULL == rlm->_timers) { CFArrayCallBacks cb = kCFTypeArrayCallBacks; cb.equal = NULL; rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb); } } if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) { __CFRunLoopTimerLock(rlt); if (NULL == rlt->_runLoop) { rlt->_runLoop = rl; } else if (rl != rlt->_runLoop) { __CFRunLoopTimerUnlock(rlt); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); return; } CFSetAddValue(rlt->_rlModes, rlm->_name); __CFRunLoopTimerUnlock(rlt); __CFRunLoopTimerFireTSRLock(); __CFRepositionTimerInMode(rlm, rlt, false); __CFRunLoopTimerFireTSRUnlock(); if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) { if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl); } } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); } } __CFRunLoopUnlock(rl); }
有上面的源码可知- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode内部就是将timer提交到当前runlopp对象的modes中的items中,注意是所有modes中的items中;
取item
等程序运行起来在断点看内存堆栈调用
(lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2 * frame #0: 0x000000010b59a240 01-Runloop初探`__28-[ViewController sourceDemo]_block_invoke(.block_descriptor=0x0000600000658318, timer=0x0000600000f58010) at ViewController.m:26 frame #1: 0x000000010b987413 Foundation`__NSFireTimer + 72 frame #2: 0x000000010bfb3b94 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20 frame #3: 0x000000010bfb3882 CoreFoundation`__CFRunLoopDoTimer + 1026 frame #4: 0x000000010bfb2eda CoreFoundation`__CFRunLoopDoTimers + 266 frame #5: 0x000000010bfadc4e CoreFoundation`__CFRunLoopRun + 2238 frame #6: 0x000000010bfad066 CoreFoundation`CFRunLoopRunSpecific + 438 frame #7: 0x00000001154c6bb0 GraphicsServices`GSEventRunModal + 65 frame #8: 0x000000010f2e0d4d UIKitCore`UIApplicationMain + 1621 frame #9: 0x000000010b59a471 01-Runloop初探`main(argc=1, argv=0x00007ffee4664cf0) at main.m:21:16 frame #10: 0x000000010d90fc25 libdyld.dylib`start + 1
可以看到在调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__之前调用了__CFRunLoopDoTimer、__CFRunLoopDoTimers、__CFRunLoopRun这三个方法,那就去源码查下看看
##### __CFRunLoopRun() 源码太长,只展示部分 static int32_t __CFRunLoopRun__CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { uint64_t startTSR = mach_absolute_time(); if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); return kCFRunLoopRunStopped; } else if (rlm->_stopped) { rlm->_stopped = false; return kCFRunLoopRunStopped; } ... //调用__CFRunLoopDoTimers() if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { // Re-arm the next timer, because we apparently fired early __CFArmNextTimerInMode(rlm, rl); } ... } ##### __CFRunLoopDoTimers() static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) { /* DOES CALLOUT */ Boolean timerHandled = false; CFMutableArrayRef timers = NULL; //遍历当前runloop中modes中的times加到CFMutableArrayRef timers对象中 for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) { CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx); if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) { if (rlt->_fireTSR <= limitTSR) { if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(timers, rlt); } } } //遍历上面的CFMutableArrayRef timers,拿到具体每个timer for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) { CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx); //调用timer Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt); timerHandled = timerHandled || did; } if (timers) CFRelease(timers); return timerHandled; } ##### __CFRunLoopDoTimer() 部分源码 static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */ Boolean timerHandled = false; uint64_t oldFireTSR = 0; /* Fire a timer */ CFRetain(rlt); __CFRunLoopTimerLock(rlt); //判断是否还在超时时间内 if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) { ... //最终调用`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__` __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info); ... } ... }
所以取(调用)的过程是:
当runloop对象在run时候,会去获取当前loop->currrentMode->timers,如果内部有timer那就遍历调用。扩展到其他的item也一样(事件源_sources0、_sources1、_observers、_timers...),也是先提交到后遍历调用。
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这有个iOS交流群:642363427,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术,iOS开发者一起交流学习成长!
注意这里currrentMode是一种也可以是多种比如commandModes就是多种mode的集合;
这个时候再看这张经典图是不是觉得很形象
runloop如何工作的
源代码详细版
还是得看__CFRunLoopRun()方法源码,但是这次无法省略了,源码有详细注释就不再赘述
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { //获取系统启动后的CPU运行时间,用于控制超时时间 uint64_t startTSR = mach_absolute_time(); // 判断当前runloop的状态是否关闭 if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); return kCFRunLoopRunStopped; } else if (rlm->_stopped) { rlm->_stopped = false; return kCFRunLoopRunStopped; } //mach端口,在内核中,消息在端口之间传递。 初始为0 mach_port_name_t dispatchPort = MACH_PORT_NULL; //判断是否为主线程 Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ))); //如果在主线程 && runloop是主线程的runloop && 该mode是commonMode,则给mach端口赋值为主线程收发消息的端口 if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF(); #if USE_DISPATCH_SOURCE_FOR_TIMERS mach_port_name_t modeQueuePort = MACH_PORT_NULL; if (rlm->_queue) { //mode赋值为dispatch端口_dispatch_runloop_root_queue_perform_4CF modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue); if (!modeQueuePort) { CRASH("Unable to get port for run loop mode queue (%d)", -1); } } #endif dispatch_source_t timeout_timer = NULL; struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context)); if (seconds <= 0.0) { // instant timeout seconds = 0.0; timeout_context->termTSR = 0ULL; // 1.0e10 == 1* 10^10 } else if (seconds <= TIMER_INTERVAL_LIMIT) { //seconds为超时时间,超时时执行__CFRunLoopTimeout函数 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT); timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_retain(timeout_timer); timeout_context->ds = timeout_timer; timeout_context->rl = (CFRunLoopRef)CFRetain(rl); timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds); dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout); dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel); uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL); dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL); dispatch_resume(timeout_timer); } else { // infinite timeout //永不超时 - 永动机 seconds = 9999999999.0; timeout_context->termTSR = UINT64_MAX; } //标志位默认为true Boolean didDispatchPortLastTime = true; //记录最后runloop状态,用于return int32_t retVal = 0; // itmes do { //初始化一个存放内核消息的缓冲池 uint8_t msg_buffer[3 * 1024]; #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI mach_msg_header_t *msg = NULL; mach_port_t livePort = MACH_PORT_NULL; #elif DEPLOYMENT_TARGET_WINDOWS HANDLE livePort = NULL; Boolean windowsMessageReceived = false; #endif //取所有需要监听的port __CFPortSet waitSet = rlm->_portSet; //设置RunLoop为可以被唤醒状态 __CFRunLoopUnsetIgnoreWakeUps(rl); /// 2\. 通知 Observers: RunLoop 即将触发 Timer 回调。 if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); if (rlm->_observerMask & kCFRunLoopBeforeSources) /// 3\. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); /// 执行被加入的block __CFRunLoopDoBlocks(rl, rlm); /// 4\. RunLoop 触发 Source0 (非port) 回调。 Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); if (sourceHandledThisLoop) { /// 执行被加入的block __CFRunLoopDoBlocks(rl, rlm); } //如果没有Sources0事件处理 并且 没有超时,poll为false //如果有Sources0事件处理 或者 超时,poll都为true Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); //第一次do..whil循环不会走该分支,因为didDispatchPortLastTime初始化是true if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI //从缓冲区读取消息 msg = (mach_msg_header_t *)msg_buffer; /// 5\. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。 if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) { //如果接收到了消息的话,前往第9步开始处理msg goto handle_msg; } #elif DEPLOYMENT_TARGET_WINDOWS if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) { goto handle_msg; } #endif } didDispatchPortLastTime = false; /// 6.通知 Observers: RunLoop 的线程即将进入休眠(sleep)。 if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); //设置RunLoop为休眠状态 __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. __CFPortSetInsert(dispatchPort, waitSet); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI #if USE_DISPATCH_SOURCE_FOR_TIMERS //这里有个内循环,用于接收等待端口的消息 //进入此循环后,线程进入休眠,直到收到新消息才跳出该循环,继续执行run loop do { if (kCFUseCollectableAllocator) { objc_clear_stack(0); memset(msg_buffer, 0, sizeof(msg_buffer)); } msg = (mach_msg_header_t *)msg_buffer; //7.接收waitSet端口的消息 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY); //收到消息之后,livePort的值为msg->msgh_local_port, 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. 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 { if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); } } else { // Go ahead and leave the inner loop. break; } } while (1); #else if (kCFUseCollectableAllocator) { objc_clear_stack(0); memset(msg_buffer, 0, sizeof(msg_buffer)); } msg = (mach_msg_header_t *)msg_buffer; /// 7\. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。 /// • 一个基于 port 的Source 的事件。 /// • 一个 Timer 到时间了 /// • RunLoop 自身的超时时间到了 /// • 被其他什么调用者手动唤醒 // mach 事务 - 指令 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY); #endif #elif DEPLOYMENT_TARGET_WINDOWS // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages. __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived); #endif __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); // Must remove 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. Also, we don't want them left // in there if this function returns. __CFPortSetRemove(dispatchPort, waitSet); __CFRunLoopSetIgnoreWakeUps(rl); // user callouts now OK again //取消runloop的休眠状态 __CFRunLoopUnsetSleeping(rl); /// 8\. 通知 Observers: RunLoop 的线程刚刚被唤醒了。 if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); /// 收到消息,处理消息。 handle_msg:; __CFRunLoopSetIgnoreWakeUps(rl); #if DEPLOYMENT_TARGET_WINDOWS if (windowsMessageReceived) { // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); if (rlm->_msgPump) { rlm->_msgPump(); } else { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) { TranslateMessage(&msg); DispatchMessage(&msg); } } __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); sourceHandledThisLoop = true; // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later. // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling. __CFRunLoopSetSleeping(rl); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL); __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); __CFRunLoopUnsetSleeping(rl); // If we have a new live port then it will be handled below as normal } #endif 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 DEPLOYMENT_TARGET_WINDOWS // Always reset the wake up port, or risk spinning forever ResetEvent(rl->_wakeUpPort); #endif } #if USE_DISPATCH_SOURCE_FOR_TIMERS else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { 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 #if USE_MK_TIMER_TOO else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled. // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754 if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { // Re-arm the next timer __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); #if DEPLOYMENT_TARGET_WINDOWS void *msg = 0; #endif __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); } #elif DEPLOYMENT_TARGET_WINDOWS sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop; #endif } } #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); #endif /// 执行加入到Loop的block __CFRunLoopDoBlocks(rl, rlm); 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。 } while (0 == retVal); if (timeout_timer) { dispatch_source_cancel(timeout_timer); dispatch_release(timeout_timer); } else { free(timeout_context); } return retVal; }
伪代码简化版
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); /// 通知 Observers: RunLoop 即将进入 loop。 __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); /// 内部函数,进入loop result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode); /// 通知 Observers: RunLoop 即将退出。 __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); return result; } /// 核心函数 static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { int32_t retVal = 0; do { /// 通知 Observers: 即将处理timer事件 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); /// 通知 Observers: 即将处理Source事件 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources) /// 处理Blocks __CFRunLoopDoBlocks(rl, rlm); /// 处理sources0 Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); /// 处理sources0返回为YES if (sourceHandledThisLoop) { /// 处理Blocks __CFRunLoopDoBlocks(rl, rlm); } /// 判断有无端口消息(Source1) if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) { /// 处理消息 goto handle_msg; } /// 通知 Observers: 即将进入休眠 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); __CFRunLoopSetSleeping(rl); /// 等待被唤醒 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy); // user callouts now OK again __CFRunLoopUnsetSleeping(rl); /// 通知 Observers: 被唤醒,结束休眠 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); handle_msg: if (被Timer唤醒) { /// 处理Timers __CFRunLoopDoTimers(rl, rlm, mach_absolute_time()); } else if (被GCD唤醒) { /// 处理gcd __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); } else if (被Source1唤醒) { /// 被Source1唤醒,处理Source1 __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) } /// 处理block __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; } // main dispatch queue __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ // __CFRunLoopDoObservers __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ // __CFRunLoopDoBlocks __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ // __CFRunLoopDoSources0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ // __CFRunLoopDoSource1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ // __CFRunLoopDoTimers __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
上面源码总结就是这么一张图
写在最后
如果文章对您有帮助,烦请点个赞,如果你对进阶底层有想法不妨动动小手,添加一下咱们的交流群642363427来获取一份详细的进阶资料为你的技术多添一份光彩。