RunLoop总结
1、每个应用都会有一个主线程,主线程对应一个runloop,保证应用一直在运行
2、default模式的RunLoop是同通过do…while循环实现的
3、获取RunLopp对象,NSRunLoop是基于NSRunLoopRef的封装
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
4、每一个Thread都对应一个RunLoop,主线程的RunLoop会在创建的时候自动创建,并且会保存在Dictionary中,线程的指针会作为key,RunLoop作为对应的value。
子线程中使用currentRunLoop会获取子线程对应的RunLoop对象,如果没有,系统会自动创建,并且保存在全局的Dictionary中。如果不获取,不会自动创建
5、一个RunLoop中包含了很多运行modes,但是只能有一个currentMode,每个Mode包含了若干个Source0、Source1、Timer、Observer
6、Mode中的Source0代表触摸事件,Source1代表基于Port的线程间通信,Timer代表定时器,Observer监听器(监听RunLoop的状态)
7、系统默认注册了5种Mode,主线程默认在DefaultMode下运行
- kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
- UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
- kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode
8、主线程中运行的Timer,如果滑动table或者TextView,Timer会停止计数,是因为Mode自动切换到了UITrackingRunLoopMode,DefaultMode下的Timer就停止运行了。可以使用KCFRunLoopCommonModes,使加入到主线程的Timer即可以在DefaultMode下运行也可以在UITrackingRunLoopMode下运行
9、使用GCD创建定时器
<colgroup><col style="width: 936px;"></colgroup>
|
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.创建一个GCD定时器
/*
第一个参数:表明创建的是一个定时器
第四个参数:队列
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 需要对timer进行强引用,保证其不会被释放掉,才会按时调用block块
// 局部变量,让指针强引用
self.timer = timer;
//2.设置定时器的开始时间,间隔时间,精准度
/*
第1个参数:要给哪个定时器设置
第2个参数:开始时间
第3个参数:间隔时间
第4个参数:精准度 一般为0 在允许范围内增加误差可提高程序的性能
GCD的单位是纳秒 所以要*NSEC_PER_SEC
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.设置定时器要执行的事情
dispatch_source_set_event_handler(timer, ^{
NSLog(@"---%@--",[NSThread currentThread]);
});
// 启动
dispatch_resume(timer);
}
10、RunLoop主要包含下面五个类
* CFRunLoopRef - 获取主RunLoop和CurrentRunLoop
* CFRunLoopModeRef - RunLoop运行模式,可以有很多,但是只能选择一个,系统默认定义了5种
* CFRunLoopSourceRef - 事件源、输入源
* CFRunLoopTimerRef - 定时器
* CFRunLoopObserverRef - 观察RunLoop运行状态
11、CFRunLoopObserverRef可以观测到RunLoop的整个生命周期,如下
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 };
12、RunLoop的处理逻辑
image.png
13、CFRunLoopRef种的使用do...while处理各种时间,先处理Timers,然后Sources、Blocks、
14、RunLoop的处理逻辑
image.png
15、RunLoop的应用,常驻线程的使用
通过创建子线程,并且在子线程中的RunLoop中加入Timer或者Source,使子线程一直存在,实现常驻线程
常驻线程可以用来记录日志,或者上传日志
17、CFRunLoop源码
https://opensource.apple.com/tarballs/CF/
16、编写测试代码
- 查看各种事件调用的source
- 编写代码查看RunLoop的运行状态
- 实现常驻线程
参考文档
1、iOS底层原理总结 - RunLoop
网友评论