之前对 runloop 的 common mod 有点模糊,今天看了下 CFRunloop 的源码,感觉清晰了不少。
TL;DR
当你调用 CFRunLoopAddCommonMode 把一个 runloop 的某个 mode 标记为 commonMode 的时候,其实做的操作就是把当前 runloop 的 commonModeItem 中的 source timer observer 注册到这个 mode 中。
当调用 CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName)
之类的方法添加一个 source/timer/observer 时,如果 modeName 是 kCFRunLoopCommonModes 的话,会把这个 source 添加到 commonModeItems 里,然后把这个 source/timer/observer 添加到 runloop 的所有 mode 中(遍历 runloop 的 modes,根据名称找到 mode,然后添加进去)
首先我们看下 CFRunLoopModeRef 的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| typedef struct __CFRunLoopMode *CFRunLoopModeRef; typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopSource * CFRunLoopSourceRef;
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopObserver * CFRunLoopObserverRef;
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
struct __CFRunLoopMode { CFRuntimeBase _base; pthread_mutex_t _lock; CFStringRef _name; Boolean _stopped; char _padding[3]; 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; 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; uint64_t _timerHardDeadline; };
|
我们再看下 CFRunLoop 的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop { CFRuntimeBase _base; pthread_mutex_t _lock; __CFPort _wakeUpPort; Boolean _unused; volatile _per_run_data *_perRunData; pthread_t _pthread; uint32_t _winthread; CFMutableSetRef _commonModes; CFMutableSetRef _commonModeItems; CFRunLoopModeRef _currentMode; CFMutableSetRef _modes; struct _block_item *_blocks_head; struct _block_item *_blocks_tail; CFAbsoluteTime _runTime; CFAbsoluteTime _sleepTime; CFTypeRef _counterpart; };
|
我们可以看到,每个 runloop 中都有若干个 modes,每个 mode 里边会有诺干个 source,timer,observer
我们可以通过下边的方法把一个 mode 标记为 common mod
1
| CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);
|
也可以通过下边的方法,把一个 source 添加或移除到 runloop 的 model 中,当然这个 mode 可以是 kCFRunLoopCommonModes
1 2 3 4 5 6
| CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName); CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName); CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode); CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName); CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName); CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
|
首先我们来看下 CFRunLoopAddCommonMode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) { CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return; __CFRunLoopLock(rl); if (!CFSetContainsValue(rl->_commonModes, modeName)) { CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL; CFSetAddValue(rl->_commonModes, modeName); if (NULL != set) { CFTypeRef context[2] = {rl, modeName};
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context); CFRelease(set); } } else {} __CFRunLoopUnlock(rl); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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); } }
|
我们看 CFRunLoopAddSource
observer 和 timer 的操作 大同小异。observer 的话由于是个数组,会有优先级的问题。 倒序遍历数组,插入到把 observer 插入到数组中,结束时,数组里的 observer 的 order 是从小到大排列的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return; if (!__CFIsValid(rls)) return; Boolean doVer0Callout = false; __CFRunLoopLock(rl); if (modeName == kCFRunLoopCommonModes) { CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; if (NULL == rl->_commonModeItems) { rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); } CFSetAddValue(rl->_commonModeItems, rls); if (NULL != set) { CFTypeRef context[2] = {rl, rls}; CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); CFRelease(set); } } else { CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true); if (NULL != rlm && NULL == rlm->_sources0) { rlm->_sources0 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); rlm->_sources1 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); rlm->_portToV1SourceMap = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); } if (NULL != rlm && !CFSetContainsValue(rlm->_sources0, rls) && !CFSetContainsValue(rlm->_sources1, rls)) { if (0 == rls->_context.version0.version) { CFSetAddValue(rlm->_sources0, rls); } else if (1 == rls->_context.version0.version) { CFSetAddValue(rlm->_sources1, rls); __CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info); if (CFPORT_NULL != src_port) { CFDictionarySetValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port, rls); __CFPortSetInsert(src_port, rlm->_portSet); } } __CFRunLoopSourceLock(rls); if (NULL == rls->_runLoops) { rls->_runLoops = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeBagCallBacks); } CFBagAddValue(rls->_runLoops, rl); __CFRunLoopSourceUnlock(rls); if (0 == rls->_context.version0.version) { if (NULL != rls->_context.version0.schedule) { doVer0Callout = true; } } } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); } } __CFRunLoopUnlock(rl); if (doVer0Callout) { rls->_context.version0.schedule(rls->_context.version0.info, rl, modeName); } }
|
总的来说呢,就是当你调用 CFRunLoopAddCommonMode
把一个 runloop 的 mode 标记为 commonMode 的时候,其实做的操作就是把当前 runloop 的 commonModeItem 中的 source timer observer 注册到这个 mode 中。在 runloop 的循环中,直接处理 mode 中的 source, timer, observer 即可