Runloop 实际应用和详细解释
来源:互联网 发布:山西网络黄河电视台 编辑:程序博客网 时间:2024/05/29 03:18
上篇文章讲了Runloop的基本原理和一些需要注意的事项,那今天来说一下Runloop在实际开发中的一些应用场景和更深刻的理解。
大家都知道我们开Timer就会出现耗时操作,那么呢耗时操作肯定不能在主线程里面,以为一旦有手势滑动触摸,UI直接卡死!
所以我们要开辟线程来做这件事,那么开辟线程我们常用的都是比较牛逼的GCD,但是我先用NSThread,这样能更清楚的看到Runloop 的应用。
这里直接上代码:
NSThread * thread = [[NSThread alloc]initWithBlock:^{
NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
//将timer 添加到Runloop里面!
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
}];[thread start];
然后呢,我在这个timer的方法里面做一个耗时操作,并且我在storyboard里面拖一个textView空间。
- (void)timerMethod{
NSLog(@”come here”);
//耗时操作 [NSThread sleepForTimeInterval:1.1];static int num = 0;NSLog(@"%d%@",num,[NSThread currentThread]);num ++;
}
此时大家可以猜一下,timer的方法会打印么?没错,答案是不会的!
虽然我们开辟了线程,而且创建了timer,也把timer添加到runloop里面了,写的很漂亮,但是,runloop在子线程执行了么?并没有执行!所以我们要让runloop在子线程中执行。那么,为什么要在子线程开启runloop呢?原因是因为线程在线程池里!!一般情况下,线程执行完任务之后cpu就不会再掉用它了!!所以要在线程里面开启runloop !!
//开辟子线程来添加timer
NSThread * thread = [[NSThread alloc]initWithBlock:^{
NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES]; //将timer 添加到Runloop里面! [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes]; //占位模式 //执行runloop!!! [[NSRunLoop currentRunLoop]run];}];[thread start];
这时候就可以欢快的打印啦,同时我滑动textView,页面也不会卡顿,因为我是开辟子线程来做耗时,一点也不影响主线程的UI操作。
现在,请注意这句代码 :[[NSRunLoop currentRunLoop]run];这是什么啊,run啊,那么runloop一旦跑起来可就是根本停不下来了-死循环啊。也就是说此时我如果这样做一件事:
//开辟子线程来添加timer
NSThread * thread = [[NSThread alloc]initWithBlock:^{
NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES]; //将timer 添加到Runloop里面! [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes]; //占位模式 //执行runloop!!! [[NSRunLoop currentRunLoop]run]; NSLog(@"子线程runloop结束了!!");}];[thread start];
此时的打印就不会出现,因为run是停不下来的,[[NSRunLoop currentRunLoop]run]; 这句代码下面不会执行了。那我们怎么来结束runloop呢?其实有很多方法:
_finished = NO;
//开辟子线程来添加timer
NSThread * thread = [[NSThread alloc]initWithBlock:^{
NSTimer * timer= [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES]; //将timer 添加到Runloop里面! // [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode]; // [[NSRunLoop currentRunLoop]addTimer:timer forMode:UITrackingRunLoopMode]; [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes]; //占位模式
// [[NSRunLoop currentRunLoop]run];
while (!_finished) { //runloop 执行0.001秒 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.0001]]; } NSLog(@"子线程runloop结束了!!");
先设置一个参数,用来判断什么时候来结束runloop,其实也可以直接暴力的干掉子线程!
下面我再用GCD实现一下:
//创建timer
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,dispatch_get_global_queue(0, 0));
//设置timer
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
//设置timer回调
dispatch_source_set_event_handler(self.timer, ^{ NSLog(@"-------%@",[NSThread currentThread]);});
//启动timer
dispatch_resume(self.timer);
熟悉GDC的朋友们知道GCD有代码块,我这里分开写注释是方便理解原理,其实GCD就相当于NSThread+Runloop,内部的原理可能不太准确但是原理基本就是这样的。
其实,还有一种牛逼的方法来实现—ReactiveCocoa 响应机制,简称RAC
首先用cocoapods来导入RAC,这个应该不用再多说了,刚刚说它牛逼在哪呢,GCD的那一坨代码可以用一句代码代替:
[[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@”—–%@”,[NSThread currentThread]);
}];
非常之屌啊,那其实GCD中的那个应用对runloop里面的source(事件源)来处理的,那么,之前介绍的runloop还有一个observer 观察者啊,那就是我们可以观察runloop的动作,直接上代码:
//获取当前的Runloop
CFRunLoopRef runloop = CFRunLoopGetCurrent();
//定义context runloop 上下文里面的参数 &CFRetain,&CFRelease是因为runloop不遵循ARC所以要进行retain 和relaese CFRunLoopObserverContext context = { 0, (__bridge void *)(self), &CFRetain, &CFRelease, NULL };// 定义观察者 static CFRunLoopObserverRef defaultModeObserver; defaultModeObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callBack, &context);
//添加当前的runloop的观察者
CFRunLoopAddObserver(runloop, defaultModeObserver, kCFRunLoopCommonModes);
下面是context里面的回调函数
//回调函数
static void callBack(){
}
这就是一个runloop的观察者的一个代码,那么这个有什么用呢,我先设置一个场景:tableViewcell复用的,每个cell需要加载3张很大的图片比如1080px*275px吧,那么屏幕一次就要加载18张图片(按照6s的尺寸),那么我们在滑动的时候界面就会很卡,不流畅。
那么这个时候,我们就可以用runloop的观察者来解决界面卡顿的性能问题:
问题原因: runloop循环一次最多要渲染18张(有可能更多)的图片
解决方案: 一次runloop只叫他渲染1张
那么,我们就要观察runloop的执行情况,观察它只要它执行一次,那我就只加载一张图片,这样就能很好的解决啦
基本的思路就是这样,我这边准备写一个demo,分享出来,会更清晰一点。
- Runloop 实际应用和详细解释
- Cport 详细解释和应用
- RunLoop 的实际应用举例-AFNetworking
- RunLoop的原理和应用
- RunLoop的原理和应用
- RunLoop的应用和RunLoop的面试题
- 神经网络图灵机的通俗解释和详细过程及应用?
- iOS Runloop详细介绍及应用示例(持续更新)
- Runloop 解释笔记
- 纯代码解释runloop
- memcache原理和实际应用
- iOS runLoop 详细解说
- malloc()和free()详细解释
- SpringMVC详细配置和解释
- sendto和recvfrom详细解释
- HTTP和HTTPS详细解释
- HttpEntity接口的详细解释与应用
- RunLoop应用场景
- 使用eclipse进行android开发
- LintCode 解题记录 17.5.31(tag:Array)
- mysql数据库5.7.*版本zip文件安装方法
- 进程与线程关系及区别
- LeetCode-599. Minimum Index Sum of Two Lists (JAVA)最小索引
- Runloop 实际应用和详细解释
- Vue的好用之处与其他框架的对比
- linux之apt-get命令
- QT学习:初接触
- 仿支付宝密码输入框
- Crazy Bird
- bzoj 1874 [BeiJing2009 WinterCamp]取石子游戏
- “考虑对方的感受”之案例
- linux下如何使用sftp命令