什么是一个Runloop
是通过内部维护的事件循环(==用户态 和 内核态 相互转换==)来对事件或消息进行管理的一个对象
事件循环是什么?
一个UI事件,一个timer,一个系统delegate。(viewDidLoad也是系统的delegate,而且要一套执行完,例如tableview的所有代理)都称之为runloop(不是NSRunloop),runloop实际上是从接收消息,然后处理完消息的一个完整过程。
NSRunloop 是什么
是CFRunloop的封装,提供了面向对象的api
CFRunloop数据结构
// runloop数据结构
struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set<CFStringRef>
CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set<CFRunLoopModeRef>
...
};
// mode数据结构
struct __CFRunLoopMode {
CFStringRef _name; // Mode名字,
CFMutableSetRef _sources0; // Set<CFRunLoopSourceRef>
CFMutableSetRef _sources1; // Set<CFRunLoopSourceRef>
CFMutableArrayRef _observers; // Array<CFRunLoopObserverRef>
CFMutableArrayRef _timers; // Array<CFRunLoopTimerRef>
...
};
//CFRunLoopModeRef 类型
1. kCFRunLoopDefaultMode: //默认mode,通常主线程在这个 Mode 下运行。
2. UITrackingRunLoopMode://追踪mode,保证Scrollview滑动顺畅不受其他 mode 影响。
3. UIInitializationRunLoopMode://启动程序后的过渡mode,启动完成后就不再使用。
4: GSEventReceiveRunLoopMode://Graphic相关事件的mode,通常用不到。
5: kCFRunLoopCommonModes://占位mode,作为标记DefaultMode和CommonMode用。
//Runloop 状态
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
};
总结:
Runloop有多个Mode(原因:为了屏蔽消息,每个Mode做当前任务下的事情),每个Mode拥有多个sources(集合),obvservers(数组),timer(数组)
mode切换和item依赖
a 主线程的runloop自动创建,子线程的runloop默认不创建(在子线程中调用NSRunLoop *runloop = [NSRunLoop currentRunLoop];
获取RunLoop对象的时候,就会创建RunLoop);b runloop退出的条件:app退出;线程关闭;设置最大时间到期;modeItem为空;
c 同一时间一个runloop只能在一个mode,切换mode只能退出runloop,再重进指定mode(隔离modeItems使之互不干扰);
d 一个item可以加到不同mode;一个mode被标记到commonModes里(这样runloop不用切换mode)。
Runloop本质:
mach port和mach_msg()。
Mach是XNU的内核,进程、线程和虚拟内存等对象通过==端口==发消息进行通信
无线程消息时
Runloop通过mach_msg()函数发送消息,如果没有port 消息,内核会将线程置于等待状态 mach_msg_trap() 。
有线程消息时
判断消息类型处理事件,并通过modeItem的callback回调
Runloop有两个关键判断点,一个是通过msg决定Runloop是否等待,一个是通过判断退出条件来决定Runloop是否循环。
Runloop处理事件模型
RunLoop 的运行逻辑
- 01、通知Observers:进入Loop
- 02、通知Observers:即将处理Timers
- 03、通知Observers:即将处理Source0
- 04、处理Source0
- 05、如果存在Source1,就跳转到第9步
- 06、通知Observers,线程即将休眠
- 07、通知Observers:开始休眠(等待消息唤醒)(source1,timer,触摸唤醒)
- 08、通知Observers:结束休眠(被某个消息唤醒)
- 09、处理消息,跳回02。处理timer,source1
- 10、通知Observers:退出Loop
source0 无法唤醒runloop,事件是在source1捕获,在source0执行