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应用
-
NSTimer
-
ImageView显示
-
PerformSelector
-
常驻线程
[[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];
}
- 自动释放池
Runloop面试相关
1.什么是Runloop?
从字面意思看,运行循环,其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如source、Timer、Observer),一个线程对应一个Runloop,主线程的Runloop默认已经启动,子线程的Runloop得手动启动(调用run方法)
2.自动释放池什么时候释放?
第一次创建:Runloop即将启动时会创建自动释放池
最后一次销毁:runloop退出的时候
其它时候的创建和销毁:当runloop即将休眠的时候会释放之前的释放池,重新创建一个新的。
3.通过Observer监听runloop的状态
4.在开发中的runloop的应用场景?
开启一个常驻线程
在子线程中开启定时器
在子线程中进行一些长期监控
可以控制定时器在特定模式下执行
可以让某些事件(行为,任务)在特定模式下执行
可以添加Observer监听Runloop的状态,比如监听点击事件的处理(在所有点击事件之前做一些操作)