借助AI辅助。
函数概述
__CFRunLoopDoSource0 是 RunLoop 中负责执行单个 Source0 回调的核心函数。它检查 source 的状态,清除标记,并在确认有效后执行回调函数。这是处理 Source0 事件的最底层实现。
与上层函数的关系
__CFRunLoopDoSources0 (批量处理)
↓
├─ 收集所有待处理的 Source0
├─ 按优先级排序
└─ 遍历调用 ──→ __CFRunLoopDoSource0 (单个处理) ⭐ 本函数
↓
执行 perform 回调
函数签名
static Boolean __CFRunLoopDoSource0(CFRunLoopRef rl, CFRunLoopSourceRef rls);
参数说明
CFRunLoopRef rl: 当前运行的 RunLoop(用于自动释放池管理)CFRunLoopSourceRef rls: 要处理的 Source0 对象
返回值
Boolean:true: 成功执行了 source 的回调函数false: source 未被处理(未标记为待处理、已无效等)
前置条件
- 调用前不持有锁: RunLoop 和 RunLoopMode 应该已经解锁
- Source 已 retain: 调用者需要保证 source 对象在调用期间不会被释放
完整代码逐行注释
static Boolean __CFRunLoopDoSource0(CFRunLoopRef rl, CFRunLoopSourceRef rls) {
// 标志位:记录 source 是否被成功处理
// true: 执行了回调函数
// false: source 未被处理(未 signal、已 invalidate 等)
Boolean sourceHandled = false;
// 🔒 锁定 source 对象(细粒度锁)
// 防止其他线程同时修改 source 的状态(如 signal、invalidate)
// 注意:此时 RunLoop 和 RunLoopMode 已经解锁(在 __CFRunLoopDoSources0 中)
__CFRunLoopSourceLock(rls);
// 🔍 检查 source 是否被标记为待处理(signaled)
// 标记方式:外部调用 CFRunLoopSourceSignal(rls) 会设置此标志位
// 如果未被标记,说明:
// 1. source 还没有事件需要处理,或
// 2. 上次处理时已经清除了标记
if (__CFRunLoopSourceIsSignaled(rls)) {
// Source 被标记为待处理,需要处理
// 🚩 清除待处理标记(unset signaled flag)
// 重要:必须在执行回调之前清除,防止重复处理
// 如果回调执行期间再次 signal,会重新设置标记,下次循环处理
__CFRunLoopSourceUnsetSignaled(rls);
// 🔍 再次检查 source 是否有效(valid)
// ⚠️ 为什么需要二次检查?
// 在 __CFRunLoopDoSources0 收集和现在执行之间,
// 其他线程可能已经调用了 CFRunLoopSourceInvalidate(rls)
//
// 时间线示例:
// t1: __CFRunLoopDoSources0 收集到 rls(此时有效)
// t2: 解锁 RunLoop 和 Mode
// t3: 其他线程调用 CFRunLoopSourceInvalidate(rls)
// t4: 本函数被调用,需要再次检查有效性
if (__CFIsValid(rls)) {
// Source 有效,可以安全执行回调
// 🔓 解锁 source
// 为什么在执行回调前解锁?
// 1. 防止死锁:回调函数中可能操作 source(如再次 signal 或 invalidate)
// 2. 提高并发:允许其他线程在回调执行期间访问 source(如检查状态)
// 3. 避免长时间持锁:回调可能耗时很长
__CFRunLoopSourceUnlock(rls);
// ---------- 提取回调信息 ----------
// 提取回调函数指针
// perform 类型:void (*)(void *info)
// version0 表示这是 Source0(非基于端口)
void *perform = rls->_context.version0.perform;
// 提取用户上下文信息(创建 source 时传入的)
// info 可以是任何用户自定义数据的指针
void *info = rls->_context.version0.info;
// ---------- 自动释放池 ----------
// 🔄 开始自动释放池(ARP = AutoRelease Pool)
// 在 ObjC 运行时环境中,管理回调中创建的临时对象
// 等价于:@autoreleasepool {
CFRUNLOOP_ARP_BEGIN(rl)
// ---------- 性能追踪 ----------
// 📊 记录性能追踪点:开始调用 Source0 回调
// 参数:事件类型、perform 函数指针、info、额外参数
// 可用 Instruments 的 System Trace 查看
cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE0 | DBG_FUNC_START, perform, info, 0, 0);
// ---------- 🎯 核心回调执行 ----------
// ⚡ 执行 Source0 的回调函数
// 宏展开后通常是:
// if (perform) {
// ((void (*)(void *))perform)(info);
// }
//
// 这是整个函数的核心目的!
//
// ⚠️ 回调中可能发生的事情:
// - 处理触摸事件(touchesBegan/Moved/Ended)
// - 执行手势识别器的 action
// - 处理自定义事件
// - 更新 UI
// - 再次操作 RunLoop(添加/移除 source、timer 等)
// - 调用 CFRunLoopSourceSignal(重新标记自己或其他 source)
// - 长时间阻塞(如果回调写得不好)
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(perform, info);
// ---------- 性能追踪结束 ----------
// 📊 记录性能追踪点:结束调用 Source0 回调
// 可计算回调执行耗时 = end - start
cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE0 | DBG_FUNC_END, perform, info, 0, 0);
// ---------- 自动释放池结束 ----------
// 🔄 结束自动释放池
// 释放回调中创建的所有 autorelease 对象
// 等价于:} // @autoreleasepool
CFRUNLOOP_ARP_END()
// ---------- Fork 检查 ----------
// 🔒 检查进程是否被 fork
// 如果在回调执行期间进程被 fork,需要重新初始化
// 这个检查放在回调后很重要,因为:
// 1. fork 可能发生在任何时候
// 2. 子进程需要清理父进程的状态(锁、线程等)
CHECK_FOR_FORK();
// ✅ 标记:成功处理了 source
sourceHandled = true;
} else {
// Source 已无效(被 invalidate)
// 不执行回调,直接解锁并返回
// 🔓 解锁 source
__CFRunLoopSourceUnlock(rls);
// sourceHandled 保持 false
}
} else {
// Source 未被标记为待处理(not signaled)
// 可能的原因:
// 1. 从未调用过 CFRunLoopSourceSignal
// 2. 上次处理时已经清除了标记,且之后没有再次 signal
// 3. 其他线程在收集和执行之间清除了标记(少见)
// 🔓 解锁 source
__CFRunLoopSourceUnlock(rls);
// sourceHandled 保持 false
}
// 返回是否成功处理了 source
// true: 执行了回调函数
// false: 未执行回调(未 signal 或已 invalidate)
return sourceHandled;
}
函数执行流程图
__CFRunLoopDoSource0
↓
🔒 Lock source
↓
┌────────┴────────┐
│ Is Signaled? │
└────────┬────────┘
│
┌─────────────┴─────────────┐
NO YES
↓ ↓
🔓 Unlock 🚩 Unset Signaled
↓ ↓
return false ┌─────────┴─────────┐
│ Is Valid? │
└─────────┬─────────┘
│
┌───────────────┴───────────────┐
NO YES
↓ ↓
🔓 Unlock 🔓 Unlock
↓ ↓
return false Extract perform & info
↓
🔄 AutoRelease Pool Begin
↓
⚡ Execute perform(info)
↓
🔄 AutoRelease Pool End
↓
🔒 Check for fork
↓
return true
使用示例
示例1:自定义事件处理
// 创建 Source0
- (CFRunLoopSourceRef)createCustomSource {
CFRunLoopSourceContext context = {0};
context.info = (__bridge void *)self;
context.perform = customSourceCallback;
CFRunLoopSourceRef source = CFRunLoopSourceCreate(
kCFAllocatorDefault,
0, // order = 0(默认优先级)
&context
);
return source;
}
// 回调函数
void customSourceCallback(void *info) {
MyObject *obj = (__bridge MyObject *)info;
[obj handleCustomEvent];
}
// 触发事件
- (void)postEvent {
CFRunLoopSourceSignal(self.source);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
}
与相关函数的对比
__CFRunLoopDoSource0 vs __CFRunLoopDoSource1
| 特性 | __CFRunLoopDoSource0 | __CFRunLoopDoSource1 |
|---|---|---|
| 事件源类型 | Source0(非基于端口) | Source1(基于 Mach Port) |
| 触发方式 | 手动 signal | 端口消息自动触发 |
| 状态检查 | IsSignaled + IsValid | 检查端口消息 |
| 回调类型 | void (*)(void *info) | 基于 Mach 消息 |
| 参数 | 用户自定义 info | Mach 消息内容 |
| 应用场景 | 触摸事件、自定义事件 | IPC、系统事件 |
总结
__CFRunLoopDoSource0 是 Source0 处理机制的最底层实现,其设计精髓在于:
- 双重状态检查: IsSignaled + IsValid,确保安全性
- 精确的标记管理: 先清除标记,再执行回调,防止丢失事件
- 细粒度锁: 只在必要时持锁,回调前解锁
- 自动释放池: 管理回调中的临时对象
- Fork 感知: 在回调后检查 fork,保证子进程正确性
这个函数虽然简洁(约 30 行),但体现了 CoreFoundation 在性能、安全性和正确性之间的精妙平衡。理解它的实现对于:
- 正确使用 Source0 API
- 避免常见的陷阱(死锁、内存泄漏、竞态条件)
- 优化事件处理性能
- 实现高质量的自定义事件系统
都至关重要。