Runloop

Runloop是什么

运行循环

基本作用:

保持程序的持续运行
处理App中的各种事件(比如触摸事件,定时器事件,Selector事件)
节省CPU资源,提高程序性能,该做事时做事,该休息时休息

在UIApplicationMain中开启一个Runloop,其实Runloop就是一个死循环

这个默认启动的Runloop是根主线程相关的。

<Foundation框架>
CFRunloopRef

<NSRunloop>
NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation)

Runloop事件源

Timer source:定时器事件

Port:基于端口的事件

custom:自定义事件源

基于Selector的事件源

Runloop对象和线程

每条线程都有唯一的一个与之对应的Runloop对象

主线程的Runloop已经自动创建好了,子线程的Runloop是需要我们主动创建的

Runloop在第一次获取的时候创建,在线程结束的时候销毁

获得Runloop对象

[NSRunloop currentRunLoop];  //获得当前线程的RunLoop对象
[NSRunloop mainRunLoop];   //获得主线程的Runloop对象
CFRunLoopGetCurrent();    //获得当前线程的RunLoop对象
CFRunLoopGetMain();     //获得主线程的RunLoop对象
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];

NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];

主线程的Runloop已经创建,但是子线程的Runloop需要手动创建

[[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

-(void)run
{
   //创建子线程对应的runloop currentRunLoop是懒加载的,如果当前线程没有对应的RunLoop,就会去创建,如果有对应的Runloop,那么就会返回 
   [NSRunLoop currentRunLoop];
}

Runloop相关类

Core Foundation中关于Runloop的5个类
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

在这里插入图片描述
在Runloop中有多个运行模式,每个Mode又包含了若干个Source\Timer\Observer但是Runloop启动之后只能选择一种运行模型

mode里面至少有一个source和timer

kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode的影响

UIInitializationRunLoopMode:在刚启动App的时候进入的第一个Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到
kCFRunLoopCommonModes:占位用的Mode,不是一种真正的Mode

-(void)timer{
   //1.创建定时器
   NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
   //2.添加定时器到runloop,指定当前的runloop模式为默认模式
   [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
   //传不同的运行模式有什么区别
   当拖拽触摸scrollView时,runLoop会自动进入到界面追踪模式,进入到界面追踪模式后,其它模式中的操作就不再理会了。
   如果想拖动的时候,让定时器工作,则可以将定时器的模式改为UITrackingRunLoopMode
   如果想两种模式下都工作,则可以添加两次。
   还有一种方法时可以将模式改为kCFRunLoopCommonModes
   凡是添加到kCFRunLoopCommonModes中的事件,都会被同时添加到打上commonModes标签的运行模式上,UITrackingRunLoopMode,kCFRunLoopDefaultMode
}

补充:创建定时器
[NSTimer scheduledTimeWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//这种方法不需要添加到runloop中,并且会设置运行模式为默认运行模式


-(void)run{

}

如果是子线程采用scheduledTimeWithTimeInterval创建定时器则需要手动添加runloop

[NSThread detachNewThreadSelector:@selector(timer2) toTarget:self withObject:nil];
-(void)timer{
   NSRunLoop *currentLoop = [NSRunLoop currentRunLoop];
   [NSTimer scheduledTimeWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
   //这种方法不需要添加到runloop中,并且会设置运行模式为默认运行模式
   [currentLoop run];
   
}

GCD中的定时器

GCD中的定时器不会受Runloop的影响

@property (nonatomic, strong) dispatch_source_t timer;

_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
//最后一个参数表示精准度,2s之后执行
 dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0);
 dispatch_source_set_event_handler(_timer, ^{
     dispatch_async(dispatch_get_main_queue(), ^{
         @strongify(self);
         [self updatePhotoProgress];
     });
 });
 dispatch_resume(_timer);

CFRunLoopSourceRef

CFRunLoopSourceRef 是事件源(输入源)
分类:按照函数调用栈来分
Source0:非基于port    用户主动触发的
Source1:基于port  系统内部的

CFRunLoopObserverRef

CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

可以监听的时间点有以下几个:

kCFRunLoopEntry                      即将进入Loop
kCFRunLoopBeforeTimer                即将处理Timer
kCFRunLoopBeforeSource               即将处理source
kCFRunLoopBeforeWaiting              即将进入休眠
kCFRunLoopAfterWaiting               刚从休眠中唤醒
kCFRunLoopExit                       即将推出Loop
//创建observer
/*
   1.怎么分配存储空间
   2.位移枚举 各种状态值
   3.是否持续监听
   4.优先级,总是传0
   5.
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAlloctorGetDefault(), 0, NO, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity){
    //当状态改变的时候调用
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

被唤醒 – 处理任务 – 处理Timer事件 – 处理source事件-- 即将进入睡眠

CFRunLoop运行流程

处理流程
Runloop应用

  1. NSTimer

  2. ImageView显示

  3. PerformSelector

  4. 常驻线程

[[NSThrread alloc] initWithTarget:self selector:@selector(task) object:nil];
[self.thread start];
-(void)task{
     NSRunLoop *runloop = [NSRunLoop currentRunLoop];
     //往runloop中添加source或者timer
     //NSTimer *timer = [NSTimer timerWithTimerInterval:2.0 target:self selector:@selector(run) userInfo:nil repeat:YES];
     //[runloop addTimer:timer forMode:NSDefaultRunLoopMode];
     [runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
     [runloop run];
     //run方法还可以给子线程设置时间,但是仅限于给子线程设置时间,主线程是不能设置时间的。
     
}
-(void)task2{

}
-(IBAction)otherBtnClick:(id)sender{
    [self performSelector:@selector(task2) onThread:self.thread withObject:nil withUnitDone:nil];
    
}
  1. 自动释放池

Runloop面试相关

1.什么是Runloop?
从字面意思看,运行循环,其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如source、Timer、Observer),一个线程对应一个Runloop,主线程的Runloop默认已经启动,子线程的Runloop得手动启动(调用run方法)

2.自动释放池什么时候释放?

第一次创建:Runloop即将启动时会创建自动释放池
最后一次销毁:runloop退出的时候
其它时候的创建和销毁:当runloop即将休眠的时候会释放之前的释放池,重新创建一个新的。

3.通过Observer监听runloop的状态

4.在开发中的runloop的应用场景?
开启一个常驻线程
在子线程中开启定时器
在子线程中进行一些长期监控
可以控制定时器在特定模式下执行
可以让某些事件(行为,任务)在特定模式下执行
可以添加Observer监听Runloop的状态,比如监听点击事件的处理(在所有点击事件之前做一些操作)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值