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)在下篇继续学习。 本篇内容可能有点绕,最后画图帮助理解(彩色框和线表示当次操作所引入的对象或关系):