(1)、什么是RunLoop
截屏2020-07-12 下午10.10.26.png 截屏2020-07-12 下午10.12.39.png 截屏2020-07-12 下午10.13.46.png 截屏2020-07-12 下午10.15.09.png 截屏2020-07-12 下午10.16.04.png(2)、RunLoop资料
- 苹果官方文档
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html - CFRunLoopRef是开源的
https://opensource.apple.com/source/CF/CF-1151.16/ -
Structure of a run loop and its sources
runloop.jpg
(3)、RunLoop与线程
- 每条线程都有唯一的一个与之对应的RunLoop对象
- 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
- RunLoop在第一次获取时创建,在线程结束时销毁
(4)、获得RunLoop对象
截屏2020-07-12 下午10.42.34.png 截屏2020-07-12 下午10.48.14.png 截屏2020-07-12 下午10.52.30.png(5)、RunLoop相关类
截屏2020-07-12 下午10.56.37.png 截屏2020-08-02 下午1.14.27.png 截屏2020-08-02 下午1.18.35.png(6)、RunLoop与相关类的关系
截屏2020-07-12 下午11.07.55.png 截屏2020-08-02 下午1.19.03.png解析:
RunLoop启动的时候必须要选择一种运行模式,当RunLoop选择了一种运行模式之后它会做一个判断做一个检查,它要检查一下这个运行模式是否为空,主要检查这个运行模式里面有没有source有没有timer,如果一个source也没有一个timer也没有它就认为这个运行模式为空,运行模式为空是这个RunLoop就马上退出了,如果运行模式不为空,里面有source或者有timer,那么这个RunLoop就启动了,也就是说这个死循环就开起来了,它就是这样运作的。注意检查的时候只会判断source和timer,它是不会判断observer的。
model(运行模式)说明:
截屏2020-07-12 下午11.18.52.png 截屏2020-07-20 下午6.06.12.png 截屏2020-07-20 下午6.24.27.png 截屏2020-07-20 下午6.27.43.png 截屏2020-07-20 下午6.31.40.png 截屏2020-07-20 下午6.33.02.png(7)、GCD中的定时器
截屏2020-07-20 下午8.48.54.png(8)、CFRunLoopSourceRef
截屏2020-07-20 下午8.56.48.png 截屏2020-07-20 下午9.09.50.png(8)、CFRunLoopObserverRef
截屏2020-07-21 上午9.47.28.png- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//01 创建观察者
/**
参数说明
第一个参数:分配存储空间 默认
第二个参数:要监听的状态
第三个参数:是否要持续监听 YES
第四个参数:0
第五个参数:block回调 当runloop状态改变的时候会调用
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
/**
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
*/
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"runloop启动");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"runloop即将处理timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"runloop即将处理source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"runloop即将进入到休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"runloop被唤醒");
break;
case kCFRunLoopExit:
NSLog(@"runloop退出");
break;
default:
break;
}
});
//02 监听runloop的状态
/**
参数说明
第一个参数:runloop对象
第二个参数:监听者
第三个参数:监听runloop在哪种运行模式下的状态
NSDefaultRunLoopMode == kCFRunLoopDefaultMode
NSRunLoopCommonModes == kCFRunLoopCommonModes
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
[NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"timer------");
}];
}
(9)、RunLoop的运行流程
RunLoop处理逻辑-网友整理版
20160811202022658.png每次运行runloop,你的线程的runloop会自动处理之前未处理的消息,并通知相关的观察者。runloop的时间队列具体的顺序如下:
1、通知观察者runloop已经启动
2、通知观察者任何即将要开始的定时器
3、通知观察者任何即将启动的非基于端口的源
4、处理任何准备好的非基于端口的源
5、如果基于端口的源准备好并处于等待状态,则立即处理,跳至步骤9
6、通知观察者线程进入休眠
7、将线程置于休眠直到任一下面的事件发生:
- 某一事件达到基于端口的源
- 定时器启动
- runloop设置的时间已经超时
- runloop被显式唤醒
8、通知观察者线程将被唤醒
9、判断唤醒的事件类型并处理:
- 如果是用户定义的定时器事件,则处理并重新开始Runloop,跳至步骤2
- 如果是一个事件源,则传递事件并重新开始Runloop,跳至步骤2
- 如果被显式唤醒,并且还没超时,则重新开始Runloop,跳至步骤2
10、通知观察者runloop结束。
截屏2020-08-02 下午3.59.41.png 截屏2020-08-02 下午3.10.49.png 截屏2020-07-21 下午12.18.16.png 截屏2020-08-02 下午5.32.11.png(10)、RunLoop的应用
截屏2020-07-21 下午12.22.05.png 截屏2020-07-21 下午2.23.47.png//如何开启一个常驻线程
- (IBAction)createNewThreadBtnClick:(id)sender {
//01 创建线程对象
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run1) object:nil];
//02 启动线程
[thread start];
self.thread = thread;
}
- (IBAction)goOnBtnClick:(id)sender {
//让之前创建的子线程继续执行任务 主线程-子线程
[self performSelector:@selector(run2) onThread:self.thread withObject:nil waitUntilDone:YES];
}
//该方法执行完毕,线程对象就会进入到死亡状态
- (void)run1 {
NSLog(@"---run1---%@",[NSThread currentThread]);
//01 子线程的runloop需要手动创建 + 启动
//02 runloop启动之后,选择运行模式(默认),判断运行模式是否为空
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
//往运行模式中添加source | timer
/**
[NSTimer scheduledTimerWithTimeInterval:3.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"tiemrunning");
}];
*/
//source = port | custom | selector
//为默认的运行模式添加一个port事件,目的是让运行模式不为空,把runloop开启起来
[runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
//内部会指定运行模式为默认
[runloop run];
NSLog(@"---end---");
}
- (void)run2 {
NSLog(@"---run2---%@",[NSThread currentThread]);
}
(11)、RunLoop自动释放
截屏2020-07-21 下午3.38.47.png(12)、RunLoop面试题
截屏2020-07-21 下午3.40.35.png 截屏2020-07-21 下午3.42.42.png 截屏2020-08-02 下午12.52.00.png 截屏2020-07-20 下午9.09.03.png 截屏2020-07-20 下午9.16.56.png1、什么是RunLoop,基本作用是什么?
字面意思:
- 运行循环、跑圈
- 其实它内部就是do-while循环,在这个循环内部不断的处理各种任务(比如source、timer、observer)
基本作用:
- 保持程序持续运行不退出
- 处理APP中各种事件(触摸事件,定时器事件,Selector事件)
- 节省CPU资源,提高程序性能,该做事的时候做事,该休息的时候休息
2、RunLoop与线程之间的关系?
- RunLoop与线程是一一对应的关系
- 主线程的RunLoop已经创建,子线程的RunLoop需要主动创建
- RunLoop第一次调用时创建,线程销毁时退出
3、RunLoop五大相关类及其之间的关系?
- CFRunloopRef
- CFRunloopModeRef
- CFRunloopTimerRef
- CFRunloopSourceRef
- CFRunloopObserverRef
RunLoop启动的时候必须要选择一种运行模式,当RunLoop选择了一种运行模式之后它会做一个判断做一个检查,它要检查一下这个运行模式是否为空,主要检查这个运行模式里面有没有source有没有timer,如果一个source也没有一个timer也没有它就认为这个运行模式为空,运行模式为空是这个RunLoop就马上退出了,如果运行模式不为空,里面有source或者有timer,那么这个RunLoop就启动了,也就是说这个死循环就开起来了,它就是这样运作的。注意检查的时候只会判断source和timer,它是不会判断observer的。
4、RunLoop的运行逻辑?
- 1、RunLoop启动
- 2、即将处理timer
- 3、即将处理source
- 4、处理source0
- 5、如果有source1,跳转到第九步
- 6、即将进入休眠
- 7、休眠中,等待被唤醒
- 8、结束休眠被唤醒
- 9、处理唤醒时收到的消息,timer消息和source1消息,然后跳转到第二步
- 10、退出RunLoop
5、RunLoop的应用有哪些?
1、 开启一个常驻线程,让子线程不被消亡,等待其它线程发来消息,处理消息事件
(1)在子线程中开启一个定时器
(2)在子线程中进行一些长期的监控
2、 可以控制定时器在特定模式下执行
3、 可以让某些事件(行为、任务)在特定模式下执行
4、 添加observer监听RunLoop状态
5、 自动释放池的创建与销毁
(1)自动释放池第一次创建:当RunLoop启动的时候
(2)自动释放池最后一次销毁:当RunLoop退出的时候
(3)自动释放池其它时间的创建和销毁:当RunLoop将要进入休眠的时候,会把之前的自动释放池释放掉,重新创建一个新的自动释放池
网友评论