2021-08-12
前言
上篇 RunLoop源代码学习(一)基本数据结构 简单过了一下 RunLoop 的相关数据结构,本篇介绍 RunLoop 的对象管理。RunLoop 的主要管理对象包括 Modes、监控对象(Sources、Observers、Timers)以及比较特殊的 Common Modes。
一、Modes管理
RunLoop 对 Modes 的管理逻辑比较简单,CFRunLoop没有公开显式的添加 Mode 的接口,也没有提供移除 Mode 的接口。
添加 Mode 是在__CFRunLoopFindMode方法中实现的,上篇有介绍过,Find Mode 会在CFRunLoop的_modes成员(CFMutableSetRef类型,元素是CFRunLoopModeRef类型)按名称查找目标 Mode,如果查找不到,则创建一个新的 Mode。
RunLoop 内部也没有用于移除的 Mode 的方法,因为需要移除 Mode 的时候,RunLoop 也该析构了,也就是说 Mode 的移除,其实是在 RunLoop 析构中实现的。总之就是,RunLoop 要么不移除任何 Mode,需要移除的时候则移除所有 Modes。
二、监控对象管理
监控对象管理逻辑中其实穿插了很多 CommonModes 管理的代码,本章节中遇到这些代码会做字体加粗处理。在第三章学习 CommonModes 管理时进行汇总。
2.1 Sources
添加目标 Source 到指定的 Mode 的操作如下:
- 目标 Mode 为 CommonModes:
- 将目标 Source 添加到 commonModeItems 集合中;
- 将目标 Source 添加到 CommonModes 所包含的所有 Modes 中;
- 目标 Mode 不是 CommonModes:
- 如果目标 Source 是 Source0:将目标 Source 添加到 Mode 的
_sources0集合中,如果目标 Source 的context.version0.schedule有值,则标记为“添加成功时需要触发操作”; - 如果目标 Source 是 Source1:将目标 Source 添加到 Mode 的
_sources1集合中,并更新 Mode 的_portToV1SourceMap字典; - 更新目标 Source 的
_runLoops集合(对所有添加了该 Source 的 RunLoop 的反向引用);
- 如果目标 Source 是 Source0:将目标 Source 添加到 Mode 的
- 如果 Source 是 Source0 且标记为“添加成功时需要触发操作”:则触发
context.version0.schedule;
NOTE:
_modes的数据类型是CFMutableSetRef,元素是CFRunLoopModeRef;_commonModes的数据类型是CFMutableSetRef,元素是CFStringRef。
NOTE:
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context)的作用是:遍历set的所有元素,以元素以及context为传参,调用__CFRunLoopAddItemToCommonModes函数。
// 省略掉一些简单的懒加载代码
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */
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;
...
//核心操作1:将目标Source添加到_commonModeItems集合中
CFSetAddValue(rl->_commonModeItems, rls);
if (NULL != set) {
CFTypeRef context[2] = {rl, rls};
//核心操作2:将目标Source添加到CommonModes所包含的所有Modes中
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
...
if (NULL != rlm && !CFSetContainsValue(rlm->_sources0, rls) && !CFSetContainsValue(rlm->_sources1, rls)) {
// 核心操作3:将目标Source添加到_source0或_source1中
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);
...
// 核心操作4:更新目标Source对“添加了该Source的所有RunLoop”的反向引用集合
CFBagAddValue(rls->_runLoops, rl);
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.schedule) {
// 核心操作5:标记为“添加成功时需要触发操作”
doVer0Callout = true;
}
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
// 核心操作6:如果标记为“添加成功时需要触发操作”,则触发_context.version0.schedule
if (doVer0Callout) {
rls->_context.version0.schedule(rls->_context.version0.info, rl, modeName); /* CALLOUT */
}
}
复制代码
将目标 Source 从指定 Mode 中移除的操作如下:
- 目标 Mode 为 CommonModes:
- 将目标 Source 从 commonModeItems 集合中移除;
- 将目标 Source 从 CommonModes 所包含的所有 Modes 中移除;
- 目标 Mode 不是 CommonModes:
- 如果目标 Source 是 Source0:将目标 Source 从 Mode 的
_sources0集合中移除,如果目标 Source 的context.version0.cancel有值,则标记为“移除成功时需要触发操作”; - 如果目标 Source 是 Source1:将目标 Source 从 Mode 的
_sources1集合中移除,并更新 Mode 的_portToV1SourceMap字典; - 更新目标 Source 的
_runLoops集合;
- 如果目标 Source 是 Source0:将目标 Source 从 Mode 的
- 如果 Source 是 Source0 且标记为“移除成功时需要触发操作”:则触发
context.version0.cancel;
NOTE:不难发现,
CFRunLoopRemoveSource和CFRunLoopAddSource的逻辑其实是高度对称的。
void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */
CHECK_FOR_FORK();
Boolean doVer0Callout = false, doRLSRelease = false;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rls)) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
//核心操作1:移除commonModeItems中的该Source
CFSetRemoveValue(rl->_commonModeItems, rls);
if (NULL != set) {
CFTypeRef context[2] = {rl, rls};
//核心操作2:遍历所有commonModes的Modes,移除目标Source
CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context);
CFRelease(set);
}
}
} else {
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false);
if (NULL != rlm && ((NULL != rlm->_sources0 && CFSetContainsValue(rlm->_sources0, rls)) || (NULL != rlm->_sources1 && CFSetContainsValue(rlm->_sources1, rls)))) {
CFRetain(rls);
if (1 == rls->_context.version0.version) {
__CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info);
if (CFPORT_NULL != src_port) {
CFDictionaryRemoveValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port);
__CFPortSetRemove(src_port, rlm->_portSet);
}
}
// 核心操作3:将目标Source从_source0或_source1中移除
CFSetRemoveValue(rlm->_sources0, rls);
CFSetRemoveValue(rlm->_sources1, rls);
__CFRunLoopSourceLock(rls);
// 核心操作4:更新目标Source对“添加了该Source的所有RunLoop”的反向引用集合
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.cancel) {
// 核心操作5:标记为“移除成功时需要触发操作”
doVer0Callout = true;
}
}
doRLSRelease = true;
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
if (doVer0Callout) {
// 核心操作6:如果标记为“移除成功时需要触发操作”,则触发_context.version0.cancel
rls->_context.version0.cancel(rls->_context.version0.info, rl, modeName); /* CALLOUT */
}
if (doRLSRelease) CFRelease(rls);
}
复制代码
2.2 Observers
添加目标 Observer 到指定的 Mode 的操作如下:
- 如果目标 Observer 的
_runLoop非空且不等于目标 RunLoop,则直接返回,以保证 Observer 只能添加到一个RunLoop中(与 Documentation 对 Observer 的描述一致); - 目标 Mode 为 CommonModes:
- 将目标 Source 添加到 commonModeItems 集合中;
- 将目标 Source 添加到 CommonModes 所包含的所有 Modes 中;
- 目标 Mode 不是 CommonModes:
- 以目标 Mode 的
_observers数组按_order降序的原则,将目标 Observer 插入_observers; - 更新目标 Mode 的
_observerMask; - 更新目标 Observer 的
_runLoop和_rlCount;
- 以目标 Mode 的
NOTE:
_observers数组是始终按_order降序的,所以 Mode 中_order越大的 Observer,处理优先级越高。不过大部分情况下_order都会设置为 0。
// 省略掉一些简单的懒加载代码
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) {
CHECK_FOR_FORK();
CFRunLoopModeRef rlm;
if (__CFRunLoopIsDeallocating(rl)) return;
// 核心操作0:保证Observer只能添加到一个RunLoop中
if (!__CFIsValid(rlo) || (NULL != rlo->_runLoop && rlo->_runLoop != rl)) return;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
...
CFSetAddValue(rl->_commonModeItems, rlo);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlo};
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
rlm = __CFRunLoopFindMode(rl, modeName, true);
...
if (NULL != rlm && !CFArrayContainsValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo)) {
Boolean inserted = false;
// 核心操作1:以目标Mode的`_observers`数组按`_order`降序的原则插入`_observers`
for (CFIndex idx = CFArrayGetCount(rlm->_observers); idx--; ) {
CFRunLoopObserverRef obs = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
if (obs->_order <= rlo->_order) {
CFArrayInsertValueAtIndex(rlm->_observers, idx + 1, rlo);
inserted = true;
break;
}
}
if (!inserted) {
CFArrayInsertValueAtIndex(rlm->_observers, 0, rlo);
}
// 核心操作2:更新目标Mode的`_observerMask`
rlm->_observerMask |= rlo->_activities;
// 核心操作3:更新目标Observer的`_runLoop`和`_rlCount`
__CFRunLoopObserverSchedule(rlo, rl, rlm);
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
static void __CFRunLoopObserverSchedule(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) {
__CFRunLoopObserverLock(rlo);
if (0 == rlo->_rlCount) {
rlo->_runLoop = rl;
}
rlo->_rlCount++;
__CFRunLoopObserverUnlock(rlo);
}
复制代码
将目标 Observer 从指定的 Mode 中移除的操作如下:
- 目标 Mode 为 CommonModes:
- 将目标 Observer 从 commonModeItems 集合中移除;
- 将目标 Observer 从 CommonModes 所包含的所有 Modes 中移除;
- 目标 Mode 不是 CommonModes:
- 将目标 Observer 从
_observers中移除; - 更新目标 Observer 的
_runLoop和_rlCount;
- 将目标 Observer 从
void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) {
CHECK_FOR_FORK();
CFRunLoopModeRef rlm;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rlo)) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
CFSetRemoveValue(rl->_commonModeItems, rlo);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlo};
CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context);
CFRelease(set);
}
} else {
}
} else {
rlm = __CFRunLoopFindMode(rl, modeName, false);
if (NULL != rlm && NULL != rlm->_observers) {
CFRetain(rlo);
CFIndex idx = CFArrayGetFirstIndexOfValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo);
if (kCFNotFound != idx) {
// 核心操作1:将目标Observer从_observers中移除
CFArrayRemoveValueAtIndex(rlm->_observers, idx);
// 核心操作2:更新目标Observer的`_runLoop`和`_rlCount`
__CFRunLoopObserverCancel(rlo, rl, rlm);
}
CFRelease(rlo);
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
static void __CFRunLoopObserverCancel(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) {
__CFRunLoopObserverLock(rlo);
rlo->_rlCount--;
if (0 == rlo->_rlCount) {
rlo->_runLoop = NULL;
}
__CFRunLoopObserverUnlock(rlo);
}
复制代码
2.3 Timers
对于三种类型的监控对象的管理,其中对 Timers 的管理的最复杂的。所以下面的某些操作,在完全了解 Timers 的管理和响应机制之前是稍微比较晦涩的。可以先暂且撇开一些内部细节,先看它到底做了哪些事情。
添加目标 Timer 到指定的 Mode 的操作如下:
- 如果目标 Timer 的
_runLoop非空且不等于目标 RunLoop,则直接返回,以保证 Timer 只能添加到一个RunLoop中(与 Documentation 对 Timer 的描述一致); - 目标 Mode 为 CommonModes:
- 将目标 Timer 添加到 commonModeItems 集合中;
- 将目标 Timer 添加到 CommonModes 所包含的所有 Modes 中;
- 目标 Mode 不是 CommonModes:
- 更新 Timer 的
_runLoop和_rlModes; - 更新目标 Mode 的
_timers的排序(包括“更新目标 Mode 的下一次计时事件触发点”操作);
- 更新 Timer 的
NOTE:关于
__CFRepositionTimerInMode、__CFArmNextTimerInMode,本篇直接阐述其实现的效果,这块属于 RunLoop 的核心逻辑,会在下一篇学习 RunLoop 如何处理 Timer 时了解到为什么需要这两个方法。
// 省略掉一些简单的懒加载代码
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);
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
...
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
...
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
__CFRunLoopTimerLock(rlt);
//核心操作1:更新Timer的_runLoop和_rlModes
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();
//核心操作2:更新目标Mode的_timers的排序
__CFRepositionTimerInMode(rlm, rlt, false);
__CFRunLoopTimerFireTSRUnlock();
//NOTE: 操作系统版本兼容代码,不重要可以忽略
if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
复制代码
将目标 Timer 从指定 Mode 中移除的操作如下:
- 目标 Mode 为 CommonModes:
- 将目标 Timer 从 commonModeItems 集合中移除;
- 将目标 Timer 从 CommonModes 所包含的所有 Modes 中移除;
- 目标 Mode 不是 CommonModes:
- 更新 Timer 的
_runLoop和_rlModes; - “更新目标 Mode 的下一次计时事件触发点”;
- 更新 Timer 的
void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
CHECK_FOR_FORK();
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rlt)) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
CFSetRemoveValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context);
CFRelease(set);
}
} else {
}
} else {
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false);
CFIndex idx = kCFNotFound;
CFMutableArrayRef timerList = NULL;
if (NULL != rlm) {
timerList = rlm->_timers;
if (NULL != timerList) {
idx = CFArrayGetFirstIndexOfValue(timerList, CFRangeMake(0, CFArrayGetCount(timerList)), rlt);
}
}
if (kCFNotFound != idx) {
__CFRunLoopTimerLock(rlt);
//核心操作1:更新Timer的_runLoop和_rlModes
CFSetRemoveValue(rlt->_rlModes, rlm->_name);
if (0 == CFSetGetCount(rlt->_rlModes)) {
rlt->_runLoop = NULL;
}
__CFRunLoopTimerUnlock(rlt);
CFArrayRemoveValueAtIndex(timerList, idx);
//核心操作2:更新目标Mode的下一次计时事件触发点
__CFArmNextTimerInMode(rlm, rl);
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
复制代码
三、CommonModes管理
总结第二章包含的 CommonModes 管理相关逻辑如下:
- 添加操作目标 Mode 为 CommonModes:
- 将目标对象添加到 commonModeItems 集合中;
- 将目标对象添加到 CommonModes 所包含的所有 Modes 中;
- 移除操作目标 Mode 为 CommonModes:
- 将目标对象从 commonModeItems 集合中移除;
- 将目标对象从 CommonModes 所包含的所有 Modes 中移除;
所调用的方法实现如下:
static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
CFStringRef modeName = (CFStringRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFTypeRef item = (CFTypeRef)(((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);
}
}
static void __CFRunLoopRemoveItemFromCommonModes(const void *value, void *ctx) {
CFStringRef modeName = (CFStringRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
CFRunLoopRemoveSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
CFRunLoopRemoveObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
CFRunLoopRemoveTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
复制代码
向 CommonModes 中添加 Mode 的代码如下。将目标 Mode 添加到 CommonModes 意味着:将 RunLoop 的_commonModeItems中的所有监控对象,添加到目标 Mode 中。
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};
//核心操作1:将_commonModeItems的所有对象,添加到目标Mode中
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
CFRelease(set);
}
} else {
}
__CFRunLoopUnlock(rl);
}
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);
}
}
复制代码
综上所述,向 CommonModes 添加某个监控对象意味着:RunLoop 在_commonModes的任意一个 Mode 下运行,都能触发该监控对象。之所以能实现这个效果源于以下三点:
- 添加目标对象到 CommonModes 时,目标对象会被添加到 CommonModes 的所有 Modes 中;
- 添加目标对象到 CommonModes 时,目标对象会被添加到 RunLoop 的 commonModeItems 中;
- 添加目标 Mode 到 CommonModes 时,会将 commonModeItems 中的所有对象添加到目标 Mode 中;
总结
本篇学习了 RunLoop 的添加 Modes、添加删除监控对象以及添加 CommonModes 基本操作。关于 Timer 管理的一些细节问题,以及 RunLoop 的核心逻辑(RunLoopRun)在下篇继续学习。 本篇内容可能有点绕,最后画图帮助理解(彩色框和线表示当次操作所引入的对象或关系):
























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)