__CFRunLoopDoObservers 函数逐行注释
函数概述
__CFRunLoopDoObservers 是 RunLoop 中负责触发观察者回调的核心函数。当 RunLoop 的状态发生变化时(如即将进入循环、即将处理 Timer、即将处理 Source 等),这个函数会被调用来通知所有注册的观察者。
函数签名
/* rl is locked, rlm is locked on entrance and exit */
static void __CFRunLoopDoObservers(CFRunLoopRef, CFRunLoopModeRef, CFRunLoopActivity) __attribute__((noinline));
注释说明
- 锁状态约定: 函数入口和出口时,
rl(RunLoop)和rlm(RunLoopMode)都必须处于加锁状态 - noinline 属性: 防止编译器内联优化此函数,可能是为了:
- 便于调试和性能分析
- 保持调用栈的可读性
- 控制代码大小
参数说明
CFRunLoopRef rl: 当前运行的 RunLoopCFRunLoopModeRef rlm: 当前的 RunLoop ModeCFRunLoopActivity activity: 当前 RunLoop 的活动状态(枚举值)
RunLoop Activity 状态枚举
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入 RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出 RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU // 所有活动状态
};
完整代码逐行注释
/* DOES CALLOUT */
// 【重要标注】此函数会执行外部回调(callout),可能导致:
// 1. 长时间阻塞(回调函数耗时)
// 2. 重入问题(回调中可能再次操作 RunLoop)
// 3. 死锁风险(因此需要在回调前解锁)
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) {
// ==================== 第一部分:性能追踪和初始化 ====================
// 📊 记录性能追踪点:开始执行 observers
// 参数:事件类型、RunLoop、Mode、活动状态、额外参数
// 可用 Instruments 的 kdebug 工具查看
cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_OBSERVERS | DBG_FUNC_START, rl, rlm, activity, 0);
// 🔒 检查进程是否被 fork
// 如果在 fork 后的子进程中,需要重新初始化 RunLoop 的锁和状态
// 防止继承父进程的锁状态导致死锁
CHECK_FOR_FORK();
// ==================== 第二部分:检查观察者数量 ====================
// 获取当前 Mode 中观察者的数量
// 三目运算符防止 _observers 为 NULL 时崩溃
CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0;
// 快速退出:如果没有观察者,直接返回
// 注意:此时仍持有锁,但不需要手动解锁(调用者会处理)
if (cnt < 1) return;
// ==================== 第三部分:分配观察者收集数组 ====================
/* Fire the observers */
// 📦 声明栈上缓冲区,避免小数组的堆分配开销
// - 如果观察者数量 ≤ 1024:在栈上分配 cnt 个元素的数组
// - 如果观察者数量 > 1024:在栈上分配 1 个元素(占位符)
// 栈分配速度快,但空间有限(通常几 MB)
STACK_BUFFER_DECL(CFRunLoopObserverRef, buffer, (cnt <= 1024) ? cnt : 1);
// 🎯 确定最终使用的数组指针
// - 小数组(≤1024):使用栈缓冲区(快速,无需释放)
// - 大数组(>1024):堆分配(慢,但可容纳更多元素)
// 1024 是经验阈值,平衡性能和栈空间使用
CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef));
// 实际收集到的观察者计数器(可能小于 cnt)
CFIndex obs_cnt = 0;
// ==================== 第四部分:收集需要触发的观察者 ====================
// 遍历 Mode 中的所有观察者
for (CFIndex idx = 0; idx < cnt; idx++) {
// 获取第 idx 个观察者对象
CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
// 🔍 三重过滤条件(必须全部满足):
// 条件1: 0 != (rlo->_activities & activity)
// 按位与检查观察者是否关注当前活动状态
// 例如:activity = kCFRunLoopBeforeTimers (0b10)
// _activities = kCFRunLoopBeforeTimers | kCFRunLoopExit (0b10000010)
// 按位与结果 = 0b10 != 0,条件成立
// 条件2: __CFIsValid(rlo)
// 检查观察者是否有效(未被 invalidate)
// 观察者可能在之前的回调中被标记为无效
// 条件3: !__CFRunLoopObserverIsFiring(rlo)
// 检查观察者是否正在执行回调
// 防止重入:如果观察者的回调函数中再次触发相同的 activity,
// 不会重复调用该观察者(避免无限递归)
if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) {
// ✅ 满足条件的观察者:
// 1. 添加到收集数组
// 2. 增加引用计数(CFRetain)
// - 防止在后续解锁期间被其他线程释放
// - 保证回调执行时对象仍然有效
// 3. obs_cnt 递增
collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);
}
}
// ==================== 第五部分:解锁(准备执行回调)====================
// 🔓 解锁 RunLoop Mode
__CFRunLoopModeUnlock(rlm);
// 🔓 解锁 RunLoop
__CFRunLoopUnlock(rl);
// ⚠️ 为什么要解锁?
// 1. 避免死锁:观察者回调可能会调用 RunLoop API(如添加 Timer、Source)
// 2. 提高并发:允许其他线程在回调执行期间访问 RunLoop
// 3. 防止长时间持锁:回调可能耗时很长(如网络请求、UI 更新)
// 🛡️ 安全性保证:
// - 已经通过 CFRetain 增加了观察者的引用计数
// - collectedObservers 数组是当前线程的局部变量
// - 即使其他线程修改了 rlm->_observers,也不会影响本次执行
// ==================== 第六部分:执行观察者回调 ====================
// 遍历收集到的观察者(注意:不是遍历 rlm->_observers)
for (CFIndex idx = 0; idx < obs_cnt; idx++) {
// 获取当前观察者
CFRunLoopObserverRef rlo = collectedObservers[idx];
// 🔒 锁定观察者对象(细粒度锁)
// 只锁定单个观察者,不影响其他观察者的并发执行
__CFRunLoopObserverLock(rlo);
// 再次检查观察者是否有效
// ⚠️ 为什么要再次检查?
// 在解锁期间,其他线程可能已经调用了 CFRunLoopObserverInvalidate
if (__CFIsValid(rlo)) {
// 检查观察者是否是一次性的(non-repeating)
// 如果是一次性的,执行完回调后需要 invalidate
Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo);
// 🚩 设置"正在执行"标志位
// 防止重入(与前面的 !__CFRunLoopObserverIsFiring 配合)
__CFRunLoopObserverSetFiring(rlo);
// 🔓 在执行回调前解锁观察者
// 原因同解锁 RunLoop:防止回调中访问观察者对象时死锁
__CFRunLoopObserverUnlock(rlo);
// ---------- 提取回调信息 ----------
// 提取回调函数指针
// 类型:void (*)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
CFRunLoopObserverCallBack callout = rlo->_callout;
// 提取用户上下文信息(创建观察者时传入的)
void *info = rlo->_context.info;
// ---------- 自动释放池 ----------
// 🔄 开始自动释放池(ARP = AutoRelease Pool)
// 在 ObjC 运行时环境中,等价于 @autoreleasepool {
// 用于自动管理回调中创建的临时对象
CFRUNLOOP_ARP_BEGIN(rl)
// ---------- 性能追踪 ----------
// 📊 记录性能追踪点:开始调用观察者回调
cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_OBSERVER | DBG_FUNC_START, callout, rlo, activity, info);
// ---------- 🎯 核心回调执行 ----------
// ⚡ 执行观察者的回调函数
// 宏定义通常是:callout(rlo, activity, info);
// 这是整个函数的核心目的!
// ⏰ 回调函数的参数:
// - rlo: 观察者对象本身
// - activity: 当前 RunLoop 活动状态(如 kCFRunLoopBeforeTimers)
// - info: 用户自定义的上下文信息
// ⚠️ 回调中可能发生的事情:
// - UI 更新(如 CA::Transaction::observer_callback)
// - 性能监控(如 FPS 检测)
// - 内存管理(如清理缓存)
// - 业务逻辑(如状态同步)
// - 再次操作 RunLoop(如添加/移除 Timer)
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(callout, rlo, activity, info);
// ---------- 性能追踪结束 ----------
// 📊 记录性能追踪点:结束调用观察者回调
// 可通过 end - start 计算回调耗时
cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_OBSERVER | DBG_FUNC_END, callout, rlo, activity, info);
// ---------- 自动释放池结束 ----------
// 🔄 结束自动释放池
// 释放回调中创建的所有 autorelease 对象
CFRUNLOOP_ARP_END()
// ---------- 处理一次性观察者 ----------
// 如果是一次性观察者(non-repeating)
if (doInvalidate) {
// ❌ 使观察者失效
// 会从 RunLoop 中移除,并标记为无效
// 后续不会再触发此观察者
CFRunLoopObserverInvalidate(rlo);
}
// 🚩 清除"正在执行"标志位
// 允许此观察者在下次 activity 时再次被触发
__CFRunLoopObserverUnsetFiring(rlo);
} else {
// 观察者在解锁期间已被其他线程 invalidate
// 🔓 解锁观察者(前面已加锁)
// 跳过回调执行
__CFRunLoopObserverUnlock(rlo);
}
// 📉 释放引用计数(对应前面的 CFRetain)
// 如果引用计数归零,观察者对象会被销毁
CFRelease(rlo);
}
// ==================== 第七部分:重新加锁 ====================
// 🔒 重新锁定 RunLoop
__CFRunLoopLock(rl);
// 🔒 重新锁定 RunLoop Mode
__CFRunLoopModeLock(rlm);
// ✅ 恢复函数入口时的锁状态
// 满足函数签名中的约定:"rl is locked, rlm is locked on entrance and exit"
// ==================== 第八部分:清理资源 ====================
// 🗑️ 释放堆内存(如果使用了 malloc)
// - 如果 collectedObservers 指向栈缓冲区(buffer),无需释放
// - 如果 collectedObservers 指向堆内存(malloc),需要手动释放
// 防止内存泄漏
if (collectedObservers != buffer) free(collectedObservers);
// ==================== 第九部分:结束性能追踪 ====================
// 📊 记录性能追踪点:完成 observers 执行
cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_OBSERVERS | DBG_FUNC_END, rl, rlm, activity, 0);
}
常见使用场景
1. UI 渲染(Core Animation)
// CA 在 kCFRunLoopBeforeWaiting 时提交渲染事务
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(
kCFAllocatorDefault,
kCFRunLoopBeforeWaiting | kCFRunLoopExit, // 监听这两个状态
YES, // 重复触发
2000000, // order = 2000000(在大多数 observer 之后)
&CA_Transaction_observerCallback, // 回调函数
NULL
);
2. 性能监控(FPS 检测)
// 监控主线程 RunLoop 的卡顿
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(
kCFAllocatorDefault,
kCFRunLoopAllActivities, // 监听所有活动
YES,
0,
&performanceMonitorCallback,
NULL
);
void performanceMonitorCallback(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void *info) {
// 记录时间戳,计算两次回调之间的间隔
// 如果间隔过长,说明发生了卡顿
}
3. 内存管理(自动释放池)
// NSRunLoop 在每次循环前后创建/销毁自动释放池
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(
kCFAllocatorDefault,
kCFRunLoopEntry | kCFRunLoopBeforeWaiting | kCFRunLoopExit,
YES,
-2147483647, // 极高优先级(负值)
&autoreleasePoolCallback,
NULL
);
总结
这个函数是理解 RunLoop 机制和 iOS 事件循环的关键,也是许多高级特性(如 UI 渲染、性能监控)的基础。