__CFRunLoopDoObservers函数详解

20 阅读8分钟

__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: 当前运行的 RunLoop
  • CFRunLoopModeRef rlm: 当前的 RunLoop Mode
  • CFRunLoopActivity 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 渲染、性能监控)的基础。