2021-08-06
前言
之前发过一篇文章,从 UIKit 事件响应入手,简单学习了一下 Runloop 的接口,当时只简单总结了一下 Runloop 的接口,并没有深入探究其原理性的东西。实际上无论是在 Apple Documentation 还是网上的很多博文中,都可以了解到 Runloop 的一些实现细节,本文则是从 Runloop 源代码中寻找些这些细节所对应的“证据”。先附上 Runloop 源代码地址:CFRunloop。
一、数据结构
数据结构是一切代码的基础。
1.1 Mode
开发过程中使用最多的 Runloop 接口想必是CFRunLoopGetCurrent
、CFRunLoopGetMain
,使用最多的相关数据结构除了CFRunLoopRef
外,就是CFRunLoopModeRef
了。我们知道每个 Runloop 都包含多个 Mode,Mode 代表了 Runloop 的运行模式,CF 公开的 Runloop Mode 只有两种:
kCFRunLoopDefaultMode
;kCFRunLoopCommonModes
;
在使用NSTimer
时,经常需要考虑是否有必要将 Timer schedule 到 CommonModes 上以防止界面滚动时,NSTimer 的回调事件被强行忽略。那么 Runloop Mode 的数据结构是什么样子的呢?源代码如下。加上简单的注释,先忽略还没学习到的 Sources 相关成员变量。
NOTE:源码中忽略了适配 Windows 平台的代码,且假设部署环境不是 Mac OS。
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
/// 标记CFRunLoopMode是Runtime基础类型
CFRuntimeBase _base;
/// 是个递归锁
pthread_mutex_t _lock;
/// 名称具有唯一性
CFStringRef _name;
/// 是否已终止
Boolean _stopped;
/// 保留位。暂时没用
char _padding[3];
#pragma mark - Mode包含的各种源及相关必备数据
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
复制代码
其中比较重要的是_name
成员,Runloop Mode 是使用名称来标记其唯一性,所以无论是 Runloop Mode 判等,还是哈希,都是使用_name
作为入参。
static Boolean __CFRunLoopModeEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFRunLoopModeRef rlm1 = (CFRunLoopModeRef)cf1;
CFRunLoopModeRef rlm2 = (CFRunLoopModeRef)cf2;
return CFEqual(rlm1->_name, rlm2->_name);
}
static CFHashCode __CFRunLoopModeHash(CFTypeRef cf) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf;
return CFHash(rlm->_name);
}
复制代码
NOTE:注意到 Source0 和 Source1 数据结构是 Set,Observers 和 Timers 的数据结构是 Array,所以初步推测单次循环中 Timer 和 Observers 的处理是必定是有序的,Source0、Source1 的处理可能是无序的。
1.2 Runloop
以下是 CF 源代码对CFRunLoopRef
的基本定义。其中_block_item
是用于将诸多void(^)(void)
类型的 block 串联成单链表的数据结构,包含_mode
成员,_block_item
可以与某个 Mode 或多个 Mode 绑定,因此_mode
可以是CFStringRef
类型,也可以是CFSetRef
类型。_per_run_data
用于记录 Runloop 的状态,其中a
、b
在构建后会被置为常数0x4346524C
,作用可以忽略。Runloop 的每次执行 Run 循环前,都会先清空其_perRunData
。
NOTE:下列源码中视为不重要的成员不作注释。Runloop Modes 相关成员也很重要,但是也没加注释,因为基本可以“顾名思义”。
typedef struct __CFRunLoop * CFRunLoopRef;
struct _block_item {
/// 下一个节点
struct _block_item *_next;
/// 绑定的Mode。注意:_mode可以是字符串,也可以是集合
CFTypeRef _mode;
/// 该节点对应的操作
void (^_block)(void);
};
typedef struct _per_run_data {
uint32_t a;
uint32_t b;
/// 标记Runloop是否已终止
uint32_t stopped;
/// 标记Runloop是否忽略外部唤醒
uint32_t ignoreWakeUps;
} _per_run_data;
struct __CFRunLoop {
CFRuntimeBase _base;
/// 递归锁,访问Modes都需要加锁
pthread_mutex_t _lock;
/// 用于唤醒该Runloop,调用CFRunLoopWakeUp需要用到
__CFPort _wakeUpPort;
Boolean _unused;
/// 记录单次Run的状态
volatile _per_run_data *_perRunData;
/// posix线程
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
/// 仅供CFRunLoopPerformBlock使用(不太重要)
struct _block_item *_blocks_head;
/// 仅供CFRunLoopPerformBlock使用(不太重要)
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
复制代码
从_modes
成员的数据类型可知,Runloop 可以包含多个 Runloop Mode。从_commonModes
成员的数据类型可知,Common Modes 并不是单一 Mode,而是多个 Runloop Mode 的集合。从_currentMode
成员的数据类型可知,Runloop 不能同时在多个 Mode 上运行。
接下来看看CFRunLoopRef
的几个基本操作。
1.2.1 状态管理
状态管理有两种方式:1、使用_base
的保留位;2、使用_perRunData
成员。两者对应的范围不相同。前者管理 Runloop 的状态,后者管理 Runloop 每次 Run 循环的状态。
例如,对 sleeping 状态的管理。其中,_base
成员是用于模拟面向对象继承,其_cfinfo
成员是一个数组,使用其CF_INFO_BITS
索引的元素的第 1 位记录CFRunLoopRef
当前是否处于休眠
CF_INLINE Boolean __CFRunLoopIsSleeping(CFRunLoopRef rl) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1);
}
CF_INLINE void __CFRunLoopSetSleeping(CFRunLoopRef rl) {
__CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
}
CF_INLINE void __CFRunLoopUnsetSleeping(CFRunLoopRef rl) {
__CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 0);
}
复制代码
类似的状态管理方式总共有三种,分别使用了_cfinfo[CF_INFO_BITS]
的三个 bit 来记录:
- stopped:第 0 位;
- sleeping:第 1 位
- deallocating:第 2 为;
最后,补充CFRuntimeBase
以及CF_INFO_BITS
的定义。
#if defined(__BIG_ENDIAN__)
#define __CF_BIG_ENDIAN__ 1
#define __CF_LITTLE_ENDIAN__ 0
#endif
#if defined(__LITTLE_ENDIAN__)
#define __CF_LITTLE_ENDIAN__ 1
#define __CF_BIG_ENDIAN__ 0
#endif
#define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__) * 3)
typedef struct __CFRuntimeBase {
uintptr_t _cfisa;
uint8_t _cfinfo[4];
#if __LP64__
uint32_t _rc;
#endif
} CFRuntimeBase;
复制代码
例如,对 stop 状态的管理:
CF_INLINE Boolean __CFRunLoopIsStopped(CFRunLoopRef rl) {
return (rl->_perRunData->stopped) ? true : false;
}
CF_INLINE void __CFRunLoopSetStopped(CFRunLoopRef rl) {
rl->_perRunData->stopped = 0x53544F50; // 'STOP'
}
CF_INLINE void __CFRunLoopUnsetStopped(CFRunLoopRef rl) {
rl->_perRunData->stopped = 0x0;
}
复制代码
CFRunLoopRef
对_perRunData
的整体操作包括两种:Push 是推入一个初始化的新的_per_run_data
实例替换旧的_perRunData
成员,并返回旧的_perRunData
成员。Pop 是用指定的_per_run_data
实例替换_perRunData
成员。
CF_INLINE volatile _per_run_data *__CFRunLoopPushPerRunData(CFRunLoopRef rl) {
volatile _per_run_data *previous = rl->_perRunData;
rl->_perRunData = (volatile _per_run_data *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(_per_run_data), 0);
rl->_perRunData->a = 0x4346524C;
rl->_perRunData->b = 0x4346524C; // 'CFRL'
rl->_perRunData->stopped = 0x00000000;
rl->_perRunData->ignoreWakeUps = 0x00000000;
return previous;
}
CF_INLINE void __CFRunLoopPopPerRunData(CFRunLoopRef rl, volatile _per_run_data *previous) {
if (rl->_perRunData) CFAllocatorDeallocate(kCFAllocatorSystemDefault, (void *)rl->_perRunData);
rl->_perRunData = previous;
}
复制代码
1.2.2 基本操作
首先是 Find Mode 操作。__CFRunLoopFindMode
用于在rl
的_modes
成员中查询,是否存在名称为modeName
的CFRunLoopMode
元素,如果不存在则构建一个名为modeName
的CFRunLoopMode
并添加到rl
的_modes
成员中。毫无疑问,CFRunLoopRunInMode
函数必定会会间接调用__CFRunLoopFindMode
。
static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeName, Boolean create) {
CHECK_FOR_FORK();
CFRunLoopModeRef rlm;
struct __CFRunLoopMode srlm;
memset(&srlm, 0, sizeof(srlm));
_CFRuntimeSetInstanceTypeIDAndIsa(&srlm, __kCFRunLoopModeTypeID);
srlm._name = modeName;
// 关键代码一:查找名为modeName的Mode
rlm = (CFRunLoopModeRef)CFSetGetValue(rl->_modes, &srlm);
if (NULL != rlm) {
__CFRunLoopModeLock(rlm);
return rlm;
}
if (!create) {
return NULL;
}
// 关键代码二:构建名为modeName的Mode
rlm = (CFRunLoopModeRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopModeTypeID, sizeof(struct __CFRunLoopMode) - sizeof(CFRuntimeBase), NULL);
if (NULL == rlm) {
return NULL;
}
__CFRunLoopLockInit(&rlm->_lock);
rlm->_name = CFStringCreateCopy(kCFAllocatorSystemDefault, modeName);
rlm->_stopped = false;
rlm->_portToV1SourceMap = NULL;
rlm->_sources0 = NULL;
rlm->_sources1 = NULL;
rlm->_observers = NULL;
rlm->_timers = NULL;
rlm->_observerMask = 0;
rlm->_portSet = __CFPortSetAllocate();
rlm->_timerSoftDeadline = UINT64_MAX;
rlm->_timerHardDeadline = UINT64_MAX;
kern_return_t ret = KERN_SUCCESS;
...
#if USE_MK_TIMER_TOO
rlm->_timerPort = mk_timer_create();
ret = __CFPortSetInsert(rlm->_timerPort, rlm->_portSet);
if (KERN_SUCCESS != ret) CRASH("*** Unable to insert timer port into port set. (%d) ***", ret);
#endif
ret = __CFPortSetInsert(rl->_wakeUpPort, rlm->_portSet);
if (KERN_SUCCESS != ret) CRASH("*** Unable to insert wake up port into port set. (%d) ***", ret);
...
// 关键代码三:将新构建的名为modeName的Mode添加到_modes成员中
CFSetAddValue(rl->_modes, rlm);
CFRelease(rlm);
__CFRunLoopModeLock(rlm); /* return mode locked */
return rlm;
}
复制代码
其次是判空操作,判空是判断 RunLoop 是否已结束的重要评判标准之一。从__CFRunLoopModeIsEmpty
的代码可以看出,其大致的判断标准是以下条件均成立:
- Source0 为空;
- Source1 为空;
- Timers 为空;
_block_item
成员中包含绑定到rlm
的 block;
NOTE:还有个关于 libdispatch 的判断条件,有点复杂,大致意思是添加到主线程 common modes 上的 mode 不会被判断为空,使主线程持续保活。
static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopModeRef previousMode) {
CHECK_FOR_FORK();
if (NULL == rlm) return true;
...
Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) return false; // represents the libdispatch main queue
if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)) return false;
if (NULL != rlm->_sources1 && 0 < CFSetGetCount(rlm->_sources1)) return false;
if (NULL != rlm->_timers && 0 < CFArrayGetCount(rlm->_timers)) return false;
struct _block_item *item = rl->_blocks_head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
doit = CFEqual(curr->_mode, rlm->_name) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(rl->_commonModes, rlm->_name));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, rlm->_name) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(rl->_commonModes, rlm->_name));
}
if (doit) return false;
}
return true;
}
复制代码
1.3 MonitorObjects
RunLoop 的可监控对象包括三种:
- Sources:由外部事件触发回调,可以通过 Signal,可以通过 Port;
- Observers:RunLoop 状态变更时触发回调;
- Timers:在特定时间点触发回调;
1.3.1 Source
RunLoop Mode 可以包含若干个源(RunLoop Source),源的类型有两种 Source0 和 Source1,Source0 使用CFRunLoopSourceSignal
触发,Source1 使用 Mach Ports 触发。Source0 和 Source1 均使用__CFRunLoopSource
表示,通过_context
联合体进行区分,联合体的两个成员的主体是一堆函数指针,用于定义源的各种基本操作。
NOTE:注意到其成员
_runLoops
为复数形式,因此 Source 可以同时被添加到多个 RunLoop 上。
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
void (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*perform)(void *info);
} CFRunLoopSourceContext;
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
#if TARGET_OS_OSX || TARGET_OS_IPHONE
mach_port_t (*getPort)(void *info);
void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
...
} CFRunLoopSourceContext1;
struct __CFRunLoopSource {
CFRuntimeBase _base;
/// 状态位
uint32_t _bits;
/// 递归锁
pthread_mutex_t _lock;
/// 序号
CFIndex _order; /* immutable */
/// 所绑定的RunLoop
CFMutableBagRef _runLoops;
/// 上下文,可以是Source0或者Source1
union {
CFRunLoopSourceContext version0; /* immutable, except invalidation */
CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
} _context;
};
复制代码
RunLoop Source 的状态管理同样使用两种方式:1、使用_base
的_cfinfo[CF_INFO_BITS]
;2、使用_bits
成员。
例如,使用方式一标记 Source 是否生效:
CF_INLINE Boolean __CFIsValid(const void *cf) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3);
}
CF_INLINE void __CFSetValid(void *cf) {
__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
}
CF_INLINE void __CFUnsetValid(void *cf) {
__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 0);
}
复制代码
例如,使用方式二标记 Source 是否收到 Signal 信号(针对 Source0);
CF_INLINE Boolean __CFRunLoopSourceIsSignaled(CFRunLoopSourceRef rls) {
return (Boolean)__CFBitfieldGetValue(rls->_bits, 1, 1);
}
CF_INLINE void __CFRunLoopSourceSetSignaled(CFRunLoopSourceRef rls) {
__CFBitfieldSetValue(rls->_bits, 1, 1, 1);
}
CF_INLINE void __CFRunLoopSourceUnsetSignaled(CFRunLoopSourceRef rls) {
__CFBitfieldSetValue(rls->_bits, 1, 1, 0);
}
复制代码
1.3.2 Observer
RunLoop Observer 可以观察 RunLoop 的状态变更。
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
} CFRunLoopObserverContext;
typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
struct __CFRunLoopObserver {
CFRuntimeBase _base;
/// 递归锁
pthread_mutex_t _lock;
/// 所观察的RunLoop
CFRunLoopRef _runLoop;
/// 所观察的RunLoop计数
CFIndex _rlCount;
CFOptionFlags _activities; /* immutable */
/// 序号
CFIndex _order; /* immutable */
/// 回调函数
CFRunLoopObserverCallBack _callout; /* immutable */
/// 上下文
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};
复制代码
RunLoop Observer 只使用_base
的_cfinfo[CF_INFO_BITS]
进行状态管理,使用了其中两个位:
- firing:第 0 位;
- repeats:第 1 位;
/* Bit 0 of the base reserved bits is used for firing state */
/* Bit 1 of the base reserved bits is used for repeats state */
CF_INLINE Boolean __CFRunLoopObserverIsFiring(CFRunLoopObserverRef rlo) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0);
}
CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 1);
}
CF_INLINE void __CFRunLoopObserverUnsetFiring(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 0);
}
CF_INLINE Boolean __CFRunLoopObserverRepeats(CFRunLoopObserverRef rlo) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1);
}
CF_INLINE void __CFRunLoopObserverSetRepeats(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
}
CF_INLINE void __CFRunLoopObserverUnsetRepeats(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 0);
}
复制代码
RunLoop Observer 的基本操作包括:1、绑定 RunLoop;2、与 RunLoop 解绑。从官方文档中可知 Observer 只能一次只能绑定到一个 RunLoop 上,但是下面的__CFRunLoopObserverSchedule
却没有这方面的限制,不过这个逻辑是在CFRunLoopAddObserver
上控制的。
NOTE:Each run loop observer can be registered in only one run loop at a time, although it can be added to multiple run loop modes within that run loop.
static void __CFRunLoopObserverSchedule(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) {
__CFRunLoopObserverLock(rlo);
if (0 == rlo->_rlCount) {
rlo->_runLoop = rl;
}
rlo->_rlCount++;
__CFRunLoopObserverUnlock(rlo);
}
static void __CFRunLoopObserverCancel(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) {
__CFRunLoopObserverLock(rlo);
rlo->_rlCount--;
if (0 == rlo->_rlCount) {
rlo->_runLoop = NULL;
}
__CFRunLoopObserverUnlock(rlo);
}
复制代码
1.3.3 Timer
至此CFRunLoopTimer
的定义实际上和前面几种源基本是相同的套路,不再赘述。
NOTE:Each run loop timer can be registered in only one run loop at a time, although it can be added to multiple run loop modes within that run loop.
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
} CFRunLoopTimerContext;
typedef void (*CFRunLoopTimerCallBack)(CFRunLoopTimerRef timer, void *info);
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
/// 递归锁
pthread_mutex_t _lock;
/// 定时器所绑定的RunLoop
CFRunLoopRef _runLoop;
/// 定时器所绑定的RunLoopMode
CFMutableSetRef _rlModes;
/// 下次触发时间点
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable */
CFTimeInterval _tolerance; /* mutable */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable */
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
/* Bit 0 of the base reserved bits is used for firing state */
/* Bit 1 of the base reserved bits is used for fired-during-callout state */
/* Bit 2 of the base reserved bits is used for waking state */
...
复制代码
二、构建和析构
该部分了解 RunLoop 及其管理对象是如何构建以及释放的。
2.1 RunLoop构建
对开发者而言,CFRunLoopRef
没有提供显式的构建方法,两个可以间接构建 RunLoop 的方法分别是CGRunLoopGetCurrent
和CFRunLoopGetMain
。从下面的源码看,是简单的懒加载的实现——获取 RunLoop 时,存在则直接返回,不存在则调用_CFRunLoopGet0
构建。
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;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
复制代码
_CFRunLoopGet0
的实现如下,具体细节注释在代码中。
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
// 核心操作1:全局记录所有runloop。若为空则构建字典和主线程RunLoop,并记录主线程RunLoop
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
// 核心操作2:若全局字典中不存在目标线程的RunLoop,则构建并记录目标线程RunLoop
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
// 核心操作3:如果目标线程是当前线程,则将RunLoop写入线程的Thread Specific Data,必要时注册RunLoop析构函数,在线程释放时析构RunLoop
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);
}
}
return loop;
}
复制代码
NOTE:上面线程记录 RunLoop 前,需要先判断目标线程是否为当前线程,原因不太确定。不过从
CFRunLoop
公开接口看,目标线程非当前线程仅出现在子线程调CFRunLoopGetMain
时,此时if
分支里面的逻辑确实没有必要执行。另外,关于PTHREAD_DESTRUCTION_ITERATIONS
定义见Manual page for pthread_key_create。
2.1.1 Observer构建
Observer 构建代码没什么干货,构建时_runLoop
为NULL
且_rlCount
为 0,因为 Observer 构建阶段显然是未与任何 RunLoop 绑定。
CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context) {
CHECK_FOR_FORK();
CFRunLoopObserverRef memory;
UInt32 size;
size = sizeof(struct __CFRunLoopObserver) - sizeof(CFRuntimeBase);
memory = (CFRunLoopObserverRef)_CFRuntimeCreateInstance(allocator, CFRunLoopObserverGetTypeID(), size, NULL);
if (NULL == memory) {
return NULL;
}
__CFSetValid(memory);
__CFRunLoopObserverUnsetFiring(memory);
if (repeats) {
__CFRunLoopObserverSetRepeats(memory);
} else {
__CFRunLoopObserverUnsetRepeats(memory);
}
__CFRunLoopLockInit(&memory->_lock);
memory->_runLoop = NULL;
memory->_rlCount = 0;
memory->_activities = activities;
memory->_order = order;
memory->_callout = callout;
if (context) {
if (context->retain) {
memory->_context.info = (void *)context->retain(context->info);
} else {
memory->_context.info = context->info;
}
memory->_context.retain = context->retain;
memory->_context.release = context->release;
memory->_context.copyDescription = context->copyDescription;
} else {
memory->_context.info = 0;
memory->_context.retain = 0;
memory->_context.release = 0;
memory->_context.copyDescription = 0;
}
return memory;
}
复制代码
2.1.2 Source构建
Source 构建也没什么干货,构建时需要 Source 类型是 Source0 还是 Source1,通过context->version
参数来指定。
CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context) {
CHECK_FOR_FORK();
CFRunLoopSourceRef memory;
uint32_t size;
if (NULL == context) CRASH("*** NULL context value passed to CFRunLoopSourceCreate(). (%d) ***", -1);
size = sizeof(struct __CFRunLoopSource) - sizeof(CFRuntimeBase);
memory = (CFRunLoopSourceRef)_CFRuntimeCreateInstance(allocator, CFRunLoopSourceGetTypeID(), size, NULL);
if (NULL == memory) {
return NULL;
}
__CFSetValid(memory);
__CFRunLoopSourceUnsetSignaled(memory);
__CFRunLoopLockInit(&memory->_lock);
memory->_bits = 0;
memory->_order = order;
memory->_runLoops = NULL;
size = 0;
switch (context->version) {
case 0:
size = sizeof(CFRunLoopSourceContext);
break;
case 1:
size = sizeof(CFRunLoopSourceContext1);
break;
}
objc_memmove_collectable(&memory->_context, context, size);
if (context->retain) {
memory->_context.version0.info = (void *)context->retain(context->info);
}
return memory;
}
复制代码
2.1.3 Timer构建
CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, CFRunLoopTimerCallBack callout, CFRunLoopTimerContext *context) {
CHECK_FOR_FORK();
if (isnan(interval)) {
CRSetCrashLogMessage("NaN was used as an interval for a CFRunLoopTimer");
HALT;
}
CFRunLoopTimerRef memory;
UInt32 size;
size = sizeof(struct __CFRunLoopTimer) - sizeof(CFRuntimeBase);
memory = (CFRunLoopTimerRef)_CFRuntimeCreateInstance(allocator, CFRunLoopTimerGetTypeID(), size, NULL);
if (NULL == memory) {
return NULL;
}
__CFSetValid(memory);
__CFRunLoopTimerUnsetFiring(memory);
__CFRunLoopLockInit(&memory->_lock);
memory->_runLoop = NULL;
memory->_rlModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
memory->_order = order;
if (interval < 0.0) interval = 0.0;
memory->_interval = interval;
memory->_tolerance = 0.0;
if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT;
memory->_nextFireDate = fireDate;
memory->_fireTSR = 0ULL;
uint64_t now2 = mach_absolute_time();
CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent();
if (fireDate < now1) {
memory->_fireTSR = now2;
} else if (TIMER_INTERVAL_LIMIT < fireDate - now1) {
memory->_fireTSR = now2 + __CFTimeIntervalToTSR(TIMER_INTERVAL_LIMIT);
} else {
memory->_fireTSR = now2 + __CFTimeIntervalToTSR(fireDate - now1);
}
memory->_callout = callout;
if (NULL != context) {
if (context->retain) {
memory->_context.info = (void *)context->retain(context->info);
} else {
memory->_context.info = context->info;
}
memory->_context.retain = context->retain;
memory->_context.release = context->release;
memory->_context.copyDescription = context->copyDescription;
} else {
memory->_context.info = 0;
memory->_context.retain = 0;
memory->_context.release = 0;
memory->_context.copyDescription = 0;
}
return memory;
}
复制代码
2.2 RunLoop析构
线程释放时,会触发 Thread Specific Data 释放,若上面的__CFRunLoopGet0
方法中将线程的 RunLoop 写入了 TSD,则线程会反复触发 TSD 所对应的析构函数(在这里是_CFSetTSD
传入的__CFFinalizeRunLoop
函数指针),直到所有 TSD 全部清空,通常最大反复尝试次数是PTHREAD_DESTRUCTOR_ITERATIONS
。
CF_PRIVATE void __CFFinalizeRunLoop(uintptr_t data) {
CFRunLoopRef rl = NULL;
// 核心操作1:解除线程和RunLoop的关联
if (data <= 1) {
__CFLock(&loopsLock);
if (__CFRunLoops) {
rl = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(pthread_self()));
if (rl) CFRetain(rl);
CFDictionaryRemoveValue(__CFRunLoops, pthreadPointer(pthread_self()));
}
__CFUnlock(&loopsLock);
} else {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(data - 1), (void (*)(void *))__CFFinalizeRunLoop);
}
if (rl && CFRunLoopGetMain() != rl) {
if (NULL != rl->_counterpart) {
CFRelease(rl->_counterpart);
rl->_counterpart = NULL;
}
// 核心操作2:移除所有RunLoopMode的Sources
CFArrayRef array = CFRunLoopCopyAllModes(rl);
for (CFIndex idx = CFArrayGetCount(array); idx--;) {
CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
__CFRunLoopRemoveAllSources(rl, modeName);
}
// 核心操作3:移除所有CommonModes的commonModeItems
__CFRunLoopRemoveAllSources(rl, kCFRunLoopCommonModes);
CFRelease(array);
}
if (rl) CFRelease(rl);
}
static void __CFRunLoopRemoveAllSources(CFRunLoopRef rl, CFStringRef modeName) {
CHECK_FOR_FORK();
CFRunLoopModeRef rlm;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
// 核心操作1:如果目标Mode为CommonModes,则移除所有CommonModes的commonModeItems,并递
// 归移除CommonModes的所有Modes的Sources
if (NULL != rl->_commonModeItems) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL != set) {
CFSetApplyFunction(set, (__CFRunLoopRemoveSourcesFromCommonMode), (void *)rl);
CFRelease(set);
}
}
} else {
rlm = __CFRunLoopFindMode(rl, modeName, false);
// 核心操作2:移除目标Mode的所有Source0
if (NULL != rlm && NULL != rlm->_sources0) {
CFSetRef set = CFSetCreateCopy(kCFAllocatorSystemDefault, rlm->_sources0);
CFTypeRef context[2] = {rl, modeName};
CFSetApplyFunction(set, (__CFRunLoopRemoveSourceFromMode), (void *)context);
CFRelease(set);
}
// 核心操作3:移除目标Mode的所有Source1
if (NULL != rlm && NULL != rlm->_sources1) {
CFSetRef set = CFSetCreateCopy(kCFAllocatorSystemDefault, rlm->_sources1);
CFTypeRef context[2] = {rl, modeName};
CFSetApplyFunction(set, (__CFRunLoopRemoveSourceFromMode), (void *)context);
CFRelease(set);
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
static void __CFRunLoopRemoveSourcesFromCommonMode(const void *value, void *ctx) {
CFStringRef modeName = (CFStringRef)value;
CFRunLoopRef rl = (CFRunLoopRef)ctx;
// 核心操作1:移除CommonMode中名称为modeName的Mode的所有Sources。所以CommonMode的移除是递归过程
__CFRunLoopRemoveAllSources(rl, modeName);
}
static void __CFRunLoopRemoveSourceFromMode(const void *value, void *ctx) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]);
// 核心操作1:移除名称为modeName的Mode的目标Source
CFRunLoopRemoveSource(rl, rls, modeName);
}
void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */
CHECK_FOR_FORK();
Boolean doVer0Callout = false, doRLSRelease = false;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
//核心操作1:如果目标Mod是CommonModes,
if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rls)) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
//核心操作1.1:移除commonModeItems中的该Source
CFSetRemoveValue(rl->_commonModeItems, rls);
if (NULL != set) {
CFTypeRef context[2] = {rl, rls};
//核心操作1.2:遍历所有commonModes的Modes,移除目标Source
CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context);
CFRelease(set);
}
}
} else {
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false);
// 核心操作2:如果目标Mode不是CommonModes,则移除目标Mode中的目标Source,Source0和
// Source1采用不同的处理方式
if (NULL != rlm && ((NULL != rlm->_sources0 && CFSetContainsValue(rlm->_sources0, rls)) || (NULL != rlm->_sources1 && CFSetContainsValue(rlm->_sources1, rls)))) {
CFRetain(rls);
// 核心操作2.1:如果是Source1类型需要先释放Port
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);
}
}
CFSetRemoveValue(rlm->_sources0, rls);
CFSetRemoveValue(rlm->_sources1, rls);
__CFRunLoopSourceLock(rls);
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
// 核心操作2.2:如果是Source0类型需要保证cancel调用
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.cancel) {
doVer0Callout = true;
}
}
doRLSRelease = true;
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
if (doVer0Callout) {
rls->_context.version0.cancel(rls->_context.version0.info, rl, modeName); /* CALLOUT */
}
if (doRLSRelease) CFRelease(rls);
}
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);
}
}
复制代码
NOTE:为什么仅主线程才需要在 Finalize 阶段就提前清空 Sources?为什么只提前清空 Sources 而 Timer 和 Observers 不需要提前清空?
上面代码的最后一句CFRelease
会间接调用到__CFRunLoopDeallocate
方法(当引用计数清零时),其实__CFRunLoopDeallocate
才是真正的 RunLoop 析构操作,基本上是释放各种源所直接持有的容器的过程。
static void __CFRunLoopDeallocate(CFTypeRef cf) {
CFRunLoopRef rl = (CFRunLoopRef)cf;
if (_CFRunLoopGet0b(pthread_main_thread_np()) == cf) HALT;
__CFRunLoopSetDeallocating(rl);
if (NULL != rl->_modes) {
// 核心操作1:释放所有Sources对runLoops的反向引用
CFSetApplyFunction(rl->_modes, (__CFRunLoopCleanseSources), rl); // remove references to rl
// 核心操作2:释放Sources、Observers、Timers
CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateSources), rl);
CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateObservers), rl);
CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateTimers), rl);
}
__CFRunLoopLock(rl);
// 核心操作3:释放Perform Blocks链表
struct _block_item *item = rl->_blocks_head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
CFRelease(curr->_mode);
Block_release(curr->_block);
free(curr);
}
if (NULL != rl->_commonModeItems) {
CFRelease(rl->_commonModeItems);
}
if (NULL != rl->_commonModes) {
CFRelease(rl->_commonModes);
}
if (NULL != rl->_modes) {
CFRelease(rl->_modes);
}
__CFPortFree(rl->_wakeUpPort);
rl->_wakeUpPort = CFPORT_NULL;
__CFRunLoopPopPerRunData(rl, NULL);
__CFRunLoopUnlock(rl);
pthread_mutex_destroy(&rl->_lock);
memset((char *)cf + sizeof(CFRuntimeBase), 0x8C, sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase));
}
/// 释放所有Sources对runLoops的反向引用
static void __CFRunLoopCleanseSources(const void *value, void *context) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)context;
CFIndex idx, cnt;
const void **list, *buffer[256];
if (NULL == rlm->_sources0 && NULL == rlm->_sources1) return;
cnt = (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0) + (rlm->_sources1 ? CFSetGetCount(rlm->_sources1) : 0);
list = (const void **)((cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0));
if (rlm->_sources0) CFSetGetValues(rlm->_sources0, list);
if (rlm->_sources1) CFSetGetValues(rlm->_sources1, list + (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0));
for (idx = 0; idx < cnt; idx++) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)list[idx];
__CFRunLoopSourceLock(rls);
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
}
/// 释放Sources
static void __CFRunLoopDeallocateSources(const void *value, void *context) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)context;
CFIndex idx, cnt;
const void **list, *buffer[256];
if (NULL == rlm->_sources0 && NULL == rlm->_sources1) return;
cnt = (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0) + (rlm->_sources1 ? CFSetGetCount(rlm->_sources1) : 0);
list = (const void **)((cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0));
if (rlm->_sources0) CFSetGetValues(rlm->_sources0, list);
if (rlm->_sources1) CFSetGetValues(rlm->_sources1, list + (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0));
for (idx = 0; idx < cnt; idx++) {
CFRetain(list[idx]);
}
if (rlm->_sources0) CFSetRemoveAllValues(rlm->_sources0);
if (rlm->_sources1) CFSetRemoveAllValues(rlm->_sources1);
for (idx = 0; idx < cnt; idx++) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)list[idx];
__CFRunLoopSourceLock(rls);
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.cancel) {
rls->_context.version0.cancel(rls->_context.version0.info, rl, rlm->_name); /* CALLOUT */
}
} else if (1 == rls->_context.version0.version) {
__CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */
if (CFPORT_NULL != port) {
__CFPortSetRemove(port, rlm->_portSet);
}
}
CFRelease(rls);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
}
static void __CFRunLoopDeallocateObservers(const void *value, void *context) {
...
}
static void __CFRunLoopDeallocateTimers(const void *value, void *context) {
...
}
复制代码
NOTE:为什么
__CFRunLoopCleanseSources
和__CFRunLoopDeallocateSources
有一半代码相同?为什么非得这样写?
总结
篇幅限制先写到这里。其实 NOTE 中仍然有些没有闹明白的问题,不过都不太重要,那些都是源代码实现上的细节处理,对理解 RunLoop 的影响是很小的。也许下次看就能闹明白了。后面再学习 RunLoop 的核心逻辑:处理各种监控对象、CommonModes 管理。