借助AI辅助。
__CFRunLoopDoSources0 函数逐行注释
函数概述
__CFRunLoopDoSources0 是 RunLoop 中负责处理 Source0 事件源的核心函数。Source0 是需要手动标记为待处理(signal)的事件源,常用于自定义事件处理、触摸事件、手势识别等场景。
Source0 与 Source1 的区别
Source0(非基于端口)
- 触发方式: 需要手动调用
CFRunLoopSourceSignal()标记为待处理,然后调用CFRunLoopWakeUp()唤醒 RunLoop - 特点: 不会自动唤醒 RunLoop,需要手动唤醒
- 使用场景: 触摸事件、自定义事件、手势识别、UIEvent 处理
- 实现: 基于回调函数
Source1(基于端口)
- 触发方式: 基于 Mach Port,当端口收到消息时自动唤醒 RunLoop
- 特点: 可以自动唤醒 RunLoop
- 使用场景: 进程间通信、系统事件、CFMachPort、CFMessagePort
- 实现: 基于 Mach 内核的端口通信
函数签名
/* rl is locked, rlm is locked on entrance and exit */
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) __attribute__((noinline));
参数说明
CFRunLoopRef rl: 当前运行的 RunLoopCFRunLoopModeRef rlm: 当前的 RunLoop ModeBoolean stopAfterHandle: 是否在处理一个 source 后就停止(用于优化性能)
返回值
Boolean: 如果至少处理了一个 source 返回true,否则返回false
前置条件
- 函数调用时必须持有锁:
rl和rlm都必须处于加锁状态 - 函数返回时保持锁状态: 出口时
rl和rlm仍然加锁
函数属性
__attribute__((noinline)): 防止编译器内联优化,便于调试和性能分析
完整代码逐行注释
/* rl is locked, rlm is locked on entrance and exit */
// 📝 锁状态约定:函数入口和出口时,rl 和 rlm 都必须处于加锁状态
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) __attribute__((noinline));
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) { /* DOES CALLOUT */
// ⚠️ 重要标注:此函数会执行外部回调(callout),可能导致:
// 1. 长时间阻塞(回调函数耗时)
// 2. 重入问题(回调中可能再次操作 RunLoop)
// 3. 死锁风险(因此需要在回调前解锁)
// ==================== 第一部分:性能追踪和初始化 ====================
// 📊 记录性能追踪点:开始处理 Source0
// 参数:事件类型、RunLoop、Mode、stopAfterHandle 标志、额外参数
// 可用 Instruments 的 kdebug 工具查看
cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_SOURCES0 | DBG_FUNC_START, rl, rlm, stopAfterHandle, 0);
// 🔒 检查进程是否被 fork
// 如果在 fork 后的子进程中,需要重新初始化 RunLoop 的锁和状态
// 防止继承父进程的锁状态导致死锁
CHECK_FOR_FORK();
// 用于存储收集到的 source(s)
// 可能是单个 CFRunLoopSourceRef 或 CFArrayRef(多个 sources)
CFTypeRef sources = NULL;
// 标志位:是否至少处理了一个 source
Boolean sourceHandled = false;
// ==================== 第二部分:收集待处理的 Source0 ====================
/* Fire the version 0 sources */
// 🔥 触发版本 0 的 sources(Source0)
// 检查当前 mode 是否有 Source0,并且数量大于 0
// rlm->_sources0 是一个 CFSet,包含所有添加到此 mode 的 Source0
if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)) {
// 📦 应用函数到 Set 中的每个元素
// CFSetApplyFunction 会遍历 _sources0 集合,对每个 source 调用 __CFRunLoopCollectSources0
// 参数说明:
// - rlm->_sources0: 要遍历的 CFSet
// - __CFRunLoopCollectSources0: 回调函数(收集器函数)
// - &sources: 上下文参数(传递给回调函数)
//
// __CFRunLoopCollectSources0 的作用:
// - 检查每个 source 是否被标记为待处理(signaled)且有效
// - 如果只有一个待处理的 source,sources 被设置为该 source
// - 如果有多个待处理的 sources,sources 被设置为包含所有待处理 sources 的数组
CFSetApplyFunction(rlm->_sources0, (__CFRunLoopCollectSources0), &sources);
}
// ==================== 第三部分:处理收集到的 Sources ====================
// 如果收集到了待处理的 source(s)
if (NULL != sources) {
// 🔓 解锁 RunLoop Mode
__CFRunLoopModeUnlock(rlm);
// 🔓 解锁 RunLoop
__CFRunLoopUnlock(rl);
// ⚠️ 为什么要解锁?
// 1. 防止死锁:source 的回调函数中可能调用 RunLoop API
// 2. 避免长时间持锁:source 的回调可能执行耗时操作
// 3. 提高并发性:允许其他线程在回调执行期间访问 RunLoop
// 🛡️ 安全性保证:
// - __CFRunLoopCollectSources0 已经对收集到的 sources 进行了 CFRetain
// - 即使其他线程修改了 rlm->_sources0,也不会影响本次执行
// sources is either a single (retained) CFRunLoopSourceRef or an array of (retained) CFRunLoopSourceRef
// sources 可能是单个(已持有)CFRunLoopSourceRef 或包含多个(已持有)CFRunLoopSourceRef 的数组
// ---------- 情况1:单个 Source ----------
// 判断 sources 是否是单个 CFRunLoopSource 对象
// CFGetTypeID() 获取对象的类型ID
if (CFGetTypeID(sources) == CFRunLoopSourceGetTypeID()) {
// 类型转换为 CFRunLoopSourceRef
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)sources;
// ⚡ 调用 __CFRunLoopDoSource0 处理单个 source
// 这个函数会:
// 1. 检查 source 是否有效且被标记为待处理
// 2. 调用 source 的回调函数
// 3. 清除 source 的待处理标记
// 返回:是否成功处理了该 source
sourceHandled = __CFRunLoopDoSource0(rl, rls);
} else {
// ---------- 情况2:多个 Sources(数组)----------
// 获取数组中 source 的数量
CFIndex cnt = CFArrayGetCount((CFArrayRef)sources);
// 📊 对 sources 数组进行排序
// 排序依据:source 的 order 字段(优先级)
// order 值越小,优先级越高,越先执行
// CFRangeMake(0, cnt) 表示对整个数组进行排序
// __CFRunLoopSourceComparator 是比较函数
CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL);
// 遍历所有待处理的 sources
for (CFIndex idx = 0; idx < cnt; idx++) {
// 获取第 idx 个 source
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx);
// ⚡ 调用 __CFRunLoopDoSource0 处理当前 source
// 返回:是否成功处理了该 source
sourceHandled = __CFRunLoopDoSource0(rl, rls);
// 🚪 提前退出优化
// 如果 stopAfterHandle 为 true 且已经处理了一个 source
// 则立即退出循环,不再处理剩余的 sources
//
// 使用场景:
// - 当 RunLoop 需要快速响应时(如处理用户输入)
// - 避免一次处理太多 sources 导致 UI 卡顿
// - 剩余的 sources 会在下次循环中继续处理
if (stopAfterHandle && sourceHandled) {
break;
}
}
}
// 📉 释放 sources 对象
// 减少引用计数(对应 __CFRunLoopCollectSources0 中的 CFRetain)
// 如果引用计数归零,对象会被销毁
CFRelease(sources);
// 🔒 重新锁定 RunLoop
__CFRunLoopLock(rl);
// 🔒 重新锁定 RunLoop Mode
__CFRunLoopModeLock(rlm);
// ✅ 恢复函数入口时的锁状态
// 满足函数签名中的约定:"rl is locked, rlm is locked on entrance and exit"
}
// ==================== 第四部分:结束性能追踪 ====================
// 📊 记录性能追踪点:完成 Source0 处理
cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_SOURCES0 | DBG_FUNC_END, rl, rlm, stopAfterHandle, 0);
// 返回是否至少处理了一个 source
// true:处理了至少一个 source
// false:没有处理任何 source(没有待处理的 sources 或所有 sources 都无效)
return sourceHandled;
}
关键设计要点
1. Source0 的生命周期
创建: CFRunLoopSourceCreate
↓
添加到 RunLoop: CFRunLoopAddSource(rl, source, mode)
↓
标记为待处理: CFRunLoopSourceSignal(source)
↓
唤醒 RunLoop: CFRunLoopWakeUp(rl)
↓
RunLoop 循环中处理: __CFRunLoopDoSources0
↓
├─ 收集待处理的 sources (__CFRunLoopCollectSources0)
├─ 按优先级排序
├─ 执行回调 (__CFRunLoopDoSource0)
└─ 清除待处理标记
↓
移除: CFRunLoopRemoveSource(rl, source, mode)
↓
销毁: CFRelease(source)
2. 锁的管理策略
入口状态:rl 锁定 + rlm 锁定
↓
收集 sources(持有锁)
↓
解锁 rl 和 rlm
↓
处理 sources(无全局锁,只锁定单个 source)
↓
重新锁定 rl 和 rlm
↓
出口状态:rl 锁定 + rlm 锁定
3. 优先级排序机制
// Source 的 order 字段决定执行顺序
CFRunLoopSourceRef source1 = CFRunLoopSourceCreate(...);
source1->_order = 100; // 后执行
CFRunLoopSourceRef source2 = CFRunLoopSourceCreate(...);
source2->_order = 0; // 先执行(默认值)
// 执行顺序:source2 -> source1
常见 order 值:
-2: 非常高优先级(系统级事件)-1: 高优先级0: 默认优先级(大多数自定义 sources)1+: 低优先级
4. stopAfterHandle 优化
// 场景1:处理所有待处理的 sources
__CFRunLoopDoSources0(rl, rlm, false); // 处理所有
// 场景2:只处理一个 source 就退出(快速响应)
__CFRunLoopDoSources0(rl, rlm, true); // 处理一个就停止
使用场景:
stopAfterHandle = false(默认):
- 正常的 RunLoop 循环
- 希望一次性处理完所有待处理的事件
stopAfterHandle = true:
- 需要快速响应新事件
- 避免长时间阻塞(处理太多 sources)
- 保持 UI 流畅性
使用场景
1. 处理触摸事件
iOS 的触摸事件系统使用 Source0:
// UIApplication 内部实现(简化版)
- (void)handleTouchEvent:(UIEvent *)event {
// 1. 系统将触摸事件封装
// 2. 创建 Source0 并标记为待处理
CFRunLoopSourceSignal(touchEventSource);
// 3. 唤醒主线程 RunLoop
CFRunLoopWakeUp(CFRunLoopGetMain());
// 4. RunLoop 循环中,__CFRunLoopDoSources0 被调用
// 5. 触摸事件的回调被执行
// 6. 事件传递给 UIView 的 touchesBegan/Moved/Ended
}
2. 自定义事件源
// 创建自定义 Source0
void performCallback(void *info) {
NSLog(@"Custom source callback: %@", (__bridge id)info);
}
CFRunLoopSourceContext context = {0};
context.info = (__bridge void *)someObject;
context.perform = performCallback;
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// 添加到 RunLoop
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 触发事件
CFRunLoopSourceSignal(source);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
// 清理
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
3. 手势识别
// UIGestureRecognizer 内部使用 Source0
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 1. 分析触摸状态
// 2. 标记手势识别器的 Source0 为待处理
CFRunLoopSourceSignal(gestureSource);
// 3. 在 RunLoop 中处理
// 4. 调用手势回调(action)
}
4. 事件分发器
@interface EventDispatcher : NSObject
@property (nonatomic, strong) NSMutableArray *pendingEvents;
@property (nonatomic, assign) CFRunLoopSourceRef source;
@end
@implementation EventDispatcher
- (instancetype)init {
self = [super init];
if (self) {
self.pendingEvents = [NSMutableArray array];
// 创建 Source0
CFRunLoopSourceContext context = {0};
context.info = (__bridge void *)self;
context.perform = dispatchEvents;
self.source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetMain(), self.source, kCFRunLoopCommonModes);
}
return self;
}
- (void)postEvent:(id)event {
@synchronized(self.pendingEvents) {
[self.pendingEvents addObject:event];
}
// 触发 Source0
CFRunLoopSourceSignal(self.source);
CFRunLoopWakeUp(CFRunLoopGetMain());
}
void dispatchEvents(void *info) {
EventDispatcher *dispatcher = (__bridge EventDispatcher *)info;
NSArray *events;
@synchronized(dispatcher.pendingEvents) {
events = [dispatcher.pendingEvents copy];
[dispatcher.pendingEvents removeAllObjects];
}
for (id event in events) {
// 处理事件
NSLog(@"Dispatch event: %@", event);
}
}
@end
实际应用案例
案例1:UIEvent 处理流程
// iOS 触摸事件处理(简化)
1. 用户触摸屏幕
↓
2. IOKit.framework 捕获硬件事件
↓
3. SpringBoard 接收事件
↓
4. 通过 IPC 发送到应用进程
↓
5. 应用的主线程创建 UIEvent
↓
6. 封装到 Source0 并 signal
CFRunLoopSourceSignal(eventSource);
CFRunLoopWakeUp(mainRunLoop);
↓
7. 主线程 RunLoop 被唤醒
↓
8. __CFRunLoopDoSources0 被调用
↓
9. 执行 event source 的回调
↓
10. UIApplication sendEvent:
↓
11. UIWindow sendEvent:
↓
12. 触摸事件传递到 UIView
- touchesBegan:withEvent:
- touchesMoved:withEvent:
- touchesEnded:withEvent:
案例2:手势识别器
// UIGestureRecognizer 内部机制
1. UITouch 事件发生
↓
2. UIGestureRecognizer 接收 touches
↓
3. 更新状态机
↓
4. 如果手势被识别,标记 Source0
CFRunLoopSourceSignal(gestureSource);
↓
5. RunLoop 处理 Source0
↓
6. 调用手势的 action
[target performSelector:action withObject:gesture];
案例3:PerformSelector 的实现
// performSelector:withObject:afterDelay: 的简化实现
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay {
if (delay == 0) {
// 立即执行:使用 Source0
PerformContext *context = [[PerformContext alloc] init];
context.target = self;
context.selector = aSelector;
context.argument = anArgument;
CFRunLoopSourceContext sourceContext = {0};
sourceContext.info = (__bridge void *)context;
sourceContext.perform = performSelectorCallback;
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &sourceContext);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
CFRunLoopSourceSignal(source);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
CFRelease(source);
} else {
// 延迟执行:使用 Timer
[NSTimer scheduledTimerWithTimeInterval:delay target:self selector:aSelector userInfo:anArgument repeats:NO];
}
}
void performSelectorCallback(void *info) {
PerformContext *context = (__bridge PerformContext *)info;
[context.target performSelector:context.selector withObject:context.argument];
}
总结
__CFRunLoopDoSources0 是 RunLoop 中处理自定义事件的核心机制,其精妙之处在于:
- 灵活的优先级系统: 通过 order 字段实现细粒度的优先级控制
- 智能的收集器设计: 单个 source 避免数组创建,优化常见场景
- 安全的锁管理: 执行回调前解锁,防止死锁和长时间持锁
- 可控的执行策略: stopAfterHandle 参数平衡吞吐量和响应性
- 高效的排序机制: 只对待处理的 sources 排序,减少不必要的开销
Source0 是 iOS 事件处理系统的基础,理解它的工作原理对于:
- 深入理解触摸事件传递机制
- 实现高性能的自定义事件系统
- 优化 RunLoop 性能
- 避免常见的陷阱和错误
都至关重要。