源码来源: https://opensource.apple.com/tarballs/CF/
使用的源码版本: CF-1151.16.tar
runloop 创建相关的函数
_CFRunLoopGet0
外部获取某个线程的 runloop 时使用
- 空值判断: 当传入线程为 空 时, 默认为主线程
if (pthread_equal(t, kNilPthreadT)) { t = pthread_main_thread_np(); } - 全局初始化:
__CFRunLoops是个全局变量, 但这个变量为空时,证明 runloop 的环境是第一次执行需要创建一些全局变量
dict: 结合后面会看到, 这是个存储了全局 runloop 的集合
mainLoop: 即主线程的 runloop, 会在第一次初始化时一块初始化, 之后会添加进 dict 中if (!__CFRunLoops) { CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); } - loop 获取: 后面会使用线程尝试获取一次 loop, 若是拿不到则创建一个, 若是能拿到则直接返回
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
// 会再拿一次, 不知道为啥, __CFRunLoopCreate中也没有加入的操作
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
}
// ...有省略
return loop;
__CFRunLoopCreate
主要是调用 _CFRuntimeCreateInstance 然后赋初始值的操作
_CFRuntimeCreateInstance:
- 内部的代码已经到 CFRuntime 这一层了, 暂时不做深入,先回到 runloop 主线
runloop 执行相关的函数
总结:
- 会发现 doXXX 函数, 最终的核心部分都是调用
__CFRUNLOOP_IS_CALLING_OUT_TO xxx的函数 __CFRUNLOOP_IS_CALLING_OUT_TO基本上所有的实现,执行外部传入函数指针, 既真正的执行外界函数
很多地方调用的CFGetTypeID(sources)是什么
下面探究的函数中, 很多地方都能看到这个判断,
虽然不是每个地方都有注释, 并且最终追溯也只能其对比的值是 0.
但是通过其逻辑和部分注释, 能得出结论 这是一个判断某个指针是**单个元素**, 还是**集合_链表_数组**的判断
(CFStringGetTypeID -> __kCFStringTypeID -> _kCFRuntimeNotATypeID -> 0)
(CFRunLoopSourceGetTypeID -> _kCFRuntimeNotATypeID -> 0)
// sources is either a single (retained) CFRunLoopSourceRef or an array of (retained) CFRunLoopSourceRef
CFGetTypeID(sources) == CFRunLoopSourceGetTypeID()
CFStringGetTypeID() == CFGetTypeID(mode)
CFStringGetTypeID() == CFGetTypeID(curr->_mode)
CFGetTypeID(item) == CFRunLoopSourceGetTypeID()
CFStringGetTypeID() == CFGetTypeID(curr->_mode)
ps.我推测其原理类似于之前研究的 runtime 的 isa 技术 其他: Tagged pointer 与 isa
__CFRunLoopDoObservers
CFRunLoop.c
核心部分是一个: for 循环取出 Observer, 循环中经过一系列操作后执行
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
这个函数会真正的调用 __CFRunLoopObserver中的回调
- 先拿 Set 中的 Observers 个数, 然后拿去申请内存, 之后逐个加入数组中(代码有省略)
// 获取个数 CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0; // 根据情况申请空间 CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef)); // 加入数组 for (CFIndex idx = 0; idx < cnt; idx++) { collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo); } - 接下来是核心部分: for 循环外部有锁操作(🔲具体锁以后视情况探究,暂时不深入)
__CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); for (CFIndex idx = 0; idx < obs_cnt; idx++) {...} __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm);
3.for 循环内部: 最终正确的会执行 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
执行过程中会调用__CFRunLoopObserverSetFiring 和 __CFRunLoopObserverUnsetFiring修改标记,追踪源码, 这是个做计算的宏
主要是这些操作:
- 验证有效性
- 验证重复
- 标记为
Firing - 核心代码: 调用
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__, 真正的执行回调 - 取消标记
Firingfor (CFIndex idx = 0; idx < obs_cnt; idx++) { CFRunLoopObserverRef rlo = collectedObservers[idx]; __CFRunLoopObserverLock(rlo); if (__CFIsValid(rlo)) { Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo); __CFRunLoopObserverSetFiring(rlo); __CFRunLoopObserverUnlock(rlo); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info); if (doInvalidate) { CFRunLoopObserverInvalidate(rlo); } __CFRunLoopObserverUnsetFiring(rlo); } else { __CFRunLoopObserverUnlock(rlo); } CFRelease(rlo); }
CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 1);
}
#define __CFBitfieldSetValue(V, N1, N2, X) ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | (((X) << (N2)) & __CFBitfieldMask(N1, N2)))
__CFRunLoopDoBlocks
CFRunLoop.c
核心逻辑是: while 遍历 block 的链表中取出 block, 然后调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
block 具体是啥我写在这: 其他: 数据结构源码解析block
CFRunLoopRef 具体数据结构:其他: 数据结构源码解析CFRunLoopRef
可以理解为, 这里就是在处理,我们 dispatch_async, dispatch_sync 所传入的 block
- 从源码解析那篇文章中可以看到, block 被以链表的形式存了起来, 这里取出了链表, 然后清掉之前存的数据
struct _block_item *head = rl->_blocks_head; struct _block_item *tail = rl->_blocks_tail; rl->_blocks_head = NULL; rl->_blocks_tail = NULL; - 之后就涉及到之前提过的
common概念, 拿到当前 common 的所有 modes
✏️ 补充 common 的连接CFSetRef commonModes = rl->_commonModes; - 进入循环
while (item),开始遍历链表, 这里直接看进入循环后 - 拿到当前节点, 并且再往下走一个元素
struct _block_item *curr = item; item = item->_next; - 判断 block 的 mode , 以决定 block 是否需要被执行
- 这里面的判断
CFGetTypeID,CFEqual等看上面 很多地方调用的CFGetTypeID(sources)是什么 - 最终外层判断出来当前 block 的 mode 是单一元素, 还是一个 set
- 单个元素执行下面判断:
- block 所属的 mode, 是否等于当前 runloop 的 mode
- 当前 runloop 的 mode 是否在
common中且 block 的 mode 是否在common中
* set 执行下面判断: - 当前 runloop 的 mode 是否在 block.mode 中
- CommonMode 是否包含了 block.mode 且 CommonMode 是否包含当前 runloop.mode
// CFRunloop.c if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) { doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode)); } else { doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode)); }6.核心 需要被执行的操作最后会调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__// 省略一些指针操作 if (doit) { __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); did = true; }
- 这里面的判断
__CFRunLoopDoSources0
核心逻辑: for 循环取出 source0, 验证后执行__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
其实与上面大同小异:
- 一些校验操作后, 判断是单个元素还是 set
- 如果是 set, 进入循环, 做校验,然后执行核心部分
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
// 把东西取出来, CFSetApplyFunction 其实内部是 do-while 宏, 主要是 __CFRunLoopCollectSources0 函数
if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)) {
CFSetApplyFunction(rlm->_sources0, (__CFRunLoopCollectSources0), &sources);
}
//循环和非循环类似, 都是执行下面的, 最终执行到`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__`
CFIndex cnt = CFArrayGetCount((CFArrayRef)sources);
CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL);
for (CFIndex idx = 0; idx < cnt; idx++) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx);
__CFRunLoopSourceLock(rls);
if (__CFRunLoopSourceIsSignaled(rls)) {
__CFRunLoopSourceUnsetSignaled(rls);
if (__CFIsValid(rls)) {
__CFRunLoopSourceUnlock(rls);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
CHECK_FOR_FORK();
sourceHandled = true;
} else {
__CFRunLoopSourceUnlock(rls);
}
} else {
__CFRunLoopSourceUnlock(rls);
}
}