RunLoop的运行逻辑
本文Demo代码可见gitHub_Demo
runloop_03.png runloop_04.png runloop_05.png查看源码:
CF-1151.16 CFRunLoop.c
1.CFRunLoopRunSpecific(RunLoop的实现)
2.CFRunLoopRunInMode(用指定的Mode启动,允许设置RunLoop超时时间)
3.__CFRunLoopRun(RunLoop的执行)
- RunLoop休眠的实现原理(如果想看更加具体的 详见:深入理解RunLoop)
-
苹果用 RunLoop 实现的功能(详见:深入理解RunLoop)
- 手势识别
- 界面更新
- 定时器
- PerformSelector
- AutoreleasePool
- 事件响应
- 关于GCD
- 网络请求
- ...
-
我们在开发中的使用
- NSTimer在滑动时停止工作的问题
- 控制线程生命周期(线程保活)
- 监控应用卡顿
- 性能优化
- ...
NSTimer失效: 我们现在界面先创建一个UITextView、 static int count = 0; NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) { NSLog(@"%d", ++count); }]; //该模式下:滚动UITextView-->timer不会打印、松开则继续打印 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //该模式下:可以正常的打印 // NSDefaultRunLoopMode、UITrackingRunLoopMode才是真正存在的模式 // NSRunLoopCommonModes并不是一个真的模式,它只是一个标记 // timer能在_commonModes数组中存放的模式下工作 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; ================================== 应用卡顿检测: 平时所说的“卡顿"主要是因为在主线程执行了比较耗时的操作 可以添加Observer到主线程RunLoop中,通过监听RunLoop状态切换的耗时,以达到监控卡顿的目的 使用:https://github.com/UIControl/LXDAppFluecyMonitor =================================== 性能优化: 使用:https://www.jianshu.com/p/30f7ced70083 Demo地址:https://github.com/qiaoyoung/RunLoop
下面我们看看怎样,线程保活?
Test01:
YDThread1.h #import <Foundation/Foundation.h> @interface YDThread1 : NSThread @end YDThread1.m #import "YDThread1.h" @implementation YDThread1 -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end TestVC01.m #import "TestVC01.h" #import "YDThread1.h" @implementation TestVC01 -(void)viewDidLoad{ [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ YDThread1 *thread = [[YDThread1 alloc]initWithTarget:self selector:@selector(run) object:nil]; [thread start]; } -(void)run{ NSLog(@"%s - %@",__func__,[NSThread currentThread]); } @end 打印: Runloop[2520:239171] -[TestVC01 run] - <YDThread1: 0x6000018c2440>{number = 3, name = (null)} Runloop[2520:239171] -[YDThread1 dealloc] Runloop[2520:239175] -[TestVC01 run] - <YDThread1: 0x6000018c1b40>{number = 4, name = (null)} Runloop[2520:239175] -[YDThread1 dealloc] Runloop[2520:239205] -[TestVC01 run] - <YDThread1: 0x6000018ce9c0>{number = 5, name = (null)} Runloop[2520:239205] -[YDThread1 dealloc] 说明: 每次点击总会创建一个子线程来处理事件(执行完会自动释放) 这样会很消耗资源
Test02:
YDThread2.h #import <Foundation/Foundation.h> @interface YDThread2 : NSThread @end YDThread2.m #import "YDThread2.h" @implementation YDThread2 -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end TestVC02.m #import "TestVC02.h" #import "YDThread2.h" @interface TestVC02 () @property (nonatomic,strong)YDThread2 *thread; @end @implementation TestVC02 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; self.thread = [[YDThread2 alloc]initWithTarget:self selector:@selector(run) object:nil]; //循环引用啦 [self.thread start]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO]; // [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES]; //YES会崩溃 //如果CPU在运行当前线程对象的时候线程任务执行完毕\异常强制退出,则当前线程对象进入死亡状态 NSLog(@"123"); } -(void)test{ NSLog(@"%s - %@",__func__,[NSThread currentThread]); } -(void)run{ NSLog(@"%s - %@",__func__,[NSThread currentThread]); } -(void)dealloc{ NSLog(@"%s",__func__); } @end 打印: Runloop[2666:248192] -[TestVC02 run] - <YDThread2: 0x600002265480>{number = 4, name = (null)} Runloop[2666:247106] 123 Runloop[2666:247106] 123 Runloop[2666:247106] 123 Runloop[2666:247106] 123 Runloop[2666:247106] 123 Runloop[2666:247106] 123
Test03:
YDThread3.h #import <Foundation/Foundation.h> @interface YDThread3 : NSThread @end YDThread3.m #import "YDThread3.h" @implementation YDThread3 -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end TestVC03.h #import <UIKit/UIKit.h> @interface TestVC03 : UIViewController @end TestVC03.m #import "TestVC03.h" #import "YDThread3.h" @interface TestVC03 () @property (nonatomic,strong)YDThread3 *thread; @end @implementation TestVC03 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; self.thread = [[YDThread3 alloc] initWithBlock:^{ NSLog(@"%s %@", __func__, [NSThread currentThread]); NSLog(@"%@----begin----", [NSThread currentThread]); // 往RunLoop里面添加Source\Timer\Observer (这样RunLoop才有效) [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; NSLog(@"%s ----end----", __func__); }]; [self.thread start]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO]; NSLog(@"123"); } -(void)test{ NSLog(@"%s - %@",__func__,[NSThread currentThread]); } /* //这样控制器可以被释放了,但是thread没有被释放 -(void)dealloc{ NSLog(@"%s",__func__); } */ /* //这样也不会被释放 代码一直会卡在 [[NSRunLoop currentRunLoop] run];这里,任务还没结束,线程就不会死 -(void)dealloc{ NSLog(@"%s",__func__); self.thread = nil ; } */ //这样 写也不对:这样只是在停掉主线程的RunLoop并非该RunLoop -(void)dealloc{ // 停止RunLoop CFRunLoopStop(CFRunLoopGetCurrent()); NSLog(@"%s",__func__); } @end
Test04:
YDThread4.h #import <Foundation/Foundation.h> @interface YDThread4 : NSThread @end YDThread4.m #import "YDThread4.h" @implementation YDThread4 -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end TestVC04.h #import <UIKit/UIKit.h> @interface TestVC04 : UIViewController @end TestVC04.m #import "TestVC04.h" #import "YDThread4.h" @interface TestVC04 () @property (nonatomic,strong)YDThread4 *thread; @end @implementation TestVC04 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; //控制器销毁了,但是线程还在 NSRunLoop的run方法是无法停止的,它专门用于开启一个永不销毁的线程(NSRunLoop) self.thread = [[YDThread4 alloc] initWithBlock:^{ NSLog(@"%s %@", __func__, [NSThread currentThread]); NSLog(@"%@----begin----", [NSThread currentThread]); // 往RunLoop里面添加Source\Timer\Observer (这样RunLoop才有效) [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; NSLog(@"%s ----end----", __func__); }]; [self.thread start]; /* //这样写:只会执行一次,自动会结束的哟 self.thread = [[YDThread4 alloc] initWithBlock:^{ NSLog(@"%@----begin----", [NSThread currentThread]); // 往RunLoop里面添加Source\Timer\Observer [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; NSLog(@"%@----end----", [NSThread currentThread]); }]; [self.thread start]; */ UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)]; [self.view addSubview:btn]; [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [btn setTitle:@"stop" forState:UIControlStateNormal]; [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO]; NSLog(@"123"); } -(void)test{ NSLog(@"%s - %@",__func__,[NSThread currentThread]); } - (void)stopClick{ [self performSelector:@selector(stop1) onThread:self.thread withObject:nil waitUntilDone:NO]; } // 用于停止子线程的RunLoop -(void)stop1{ // 停止RunLoop CFRunLoopStop(CFRunLoopGetCurrent()); NSLog(@"%s - %@",__func__,[NSThread currentThread]); } -(void)dealloc{ NSLog(@"%s",__func__); } @end
Test05:
YDThread5.h #import <Foundation/Foundation.h> @interface YDThread5 : NSThread @end YDThread5.m #import "YDThread5.h" @implementation YDThread5 -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end TestVC05.h #import <UIKit/UIKit.h> @interface TestVC05 : UIViewController @end TestVC05.m #import "TestVC05.h" #import "YDThread5.h" @interface TestVC05 () @property (nonatomic,strong)YDThread5 *thread; @property (assign, nonatomic, getter=isStoped) BOOL stopped; @end @implementation TestVC05 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; __weak typeof(self) weakSelf = self; self.stopped = NO; self.thread = [[YDThread5 alloc] initWithBlock:^{ NSLog(@"%s %@", __func__, [NSThread currentThread]); NSLog(@"%@----begin----", [NSThread currentThread]); while (!weakSelf.isStoped) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } NSLog(@"%s ----end----", __func__); }]; [self.thread start]; UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)]; [self.view addSubview:btn]; [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [btn setTitle:@"stop" forState:UIControlStateNormal]; [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO]; NSLog(@"123"); } -(void)test{ NSLog(@"%s - %@",__func__,[NSThread currentThread]); } - (void)stopClick{ [self performSelector:@selector(stop1) onThread:self.thread withObject:nil waitUntilDone:NO]; } // 用于停止子线程的RunLoop -(void)stop1{ // 设置标记为NO self.stopped = YES; // 停止RunLoop CFRunLoopStop(CFRunLoopGetCurrent()); NSLog(@"%s - %@",__func__,[NSThread currentThread]); //这样:主动点击stop,线程会跟随控制器而销毁, //但是如果直接点击返回,会崩溃, // [self performSelector:@selector(stop1) onThread:self.thread withObject:nil waitUntilDone:NO]; //这样并未等子线程执行完 就执行了 "}"了,之后执行dealloc方法,控制器为nil,所以会出现坏内存访问 (vc没了) } -(void)dealloc{ NSLog(@"%s",__func__); [self stopClick]; } @end
Test06(终结版本):
YDThread6.h #import <Foundation/Foundation.h> @interface YDThread6 : NSThread @end YDThread6.m #import "YDThread6.h" @implementation YDThread6 -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end TestVC06.h #import <UIKit/UIKit.h> @interface TestVC06 : UIViewController @end TestVC06.m #import "TestVC06.h" #import "YDThread6.h" @interface TestVC06 () @property (nonatomic,strong)YDThread6 *thread; @property (assign, nonatomic, getter=isStoped) BOOL stopped; @end @implementation TestVC06 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; __weak typeof(self) weakSelf = self; self.stopped = NO; self.thread = [[YDThread6 alloc] initWithBlock:^{ NSLog(@"%@----begin----", [NSThread currentThread]); while (weakSelf && !weakSelf.isStoped) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } NSLog(@"%s ----end----", __func__); }]; [self.thread start]; UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)]; [self.view addSubview:btn]; [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [btn setTitle:@"stop" forState:UIControlStateNormal]; [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ if (!self.thread) return; [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO]; } -(void)test{ NSLog(@"%s - %@",__func__,[NSThread currentThread]); } -(void)stopClick{ if (!self.thread) return; //在子线程调用stop(waitUntilDone设置为YES,代表子线程的代码执行完毕后,这个方法才会往下走) [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES]; } -(void)stopThread{ // 设置标记为NO self.stopped = YES; // 停止RunLoop CFRunLoopStop(CFRunLoopGetCurrent()); NSLog(@"%s - %@",__func__,[NSThread currentThread]); // 清空线程 self.thread = nil; } -(void)dealloc{ NSLog(@"%s",__func__); [self stopClick]; } @end
封装
YDPermenantThread.h #import <Foundation/Foundation.h> typedef void (^YDPermenantThreadTask)(void); @interface YDPermenantThread : NSObject /** 在当前子线程执行一个任务 */ - (void)executeTask:(YDPermenantThreadTask)task; /** 结束线程 */ - (void)stop; @end YDPermenantThread.m #import "YDPermenantThread.h" @interface YDThread : NSThread @end @implementation YDThread -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end @interface YDPermenantThread() @property (nonatomic,strong)YDThread *myThread; @property (assign, nonatomic, getter=isStopped) BOOL stopped; @end @implementation YDPermenantThread -(instancetype)init{ if(self = [super init]){ self.stopped = NO; __weak typeof(self) weakSelf = self; self.myThread = [[YDThread alloc]initWithBlock:^{ [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode]; while (weakSelf && !weakSelf.isStopped) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } }]; [self.myThread start]; }return self; } - (void)executeTask:(YDPermenantThreadTask)task{ if(!self.myThread || !task)return; [self performSelector:@selector(__executeTask:) onThread:self.myThread withObject:task waitUntilDone:NO]; } - (void)stop{ if (!self.myThread) return; [self performSelector:@selector(__stop) onThread:self.myThread withObject:nil waitUntilDone:YES]; } - (void)dealloc{ NSLog(@"%s", __func__); [self stop]; } #pragma mark - private methods - (void)__stop{ self.stopped = YES; CFRunLoopStop(CFRunLoopGetCurrent()); self.myThread = nil; } - (void)__executeTask:(YDPermenantThreadTask)task{ task(); } @end TestVC07.h #import <UIKit/UIKit.h> @interface TestVC07 : UIViewController @end TestVC07.m #import "TestVC07.h" #import "YDPermenantThread.h" @interface TestVC07 () @property (nonatomic,strong)YDPermenantThread *thread; @end @implementation TestVC07 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; self.thread = [[YDPermenantThread alloc]init]; UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 60)]; [self.view addSubview:btn]; [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [btn setTitle:@"stop" forState:UIControlStateNormal]; [btn addTarget:self action:@selector(stopClick) forControlEvents:UIControlEventTouchUpInside]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self.thread executeTask:^{ NSLog(@"Do something- %@", [NSThread currentThread]); }]; } -(void)stopClick{ [self.thread stop]; } -(void)dealloc{ NSLog(@"%s",__FUNCTION__); } @end
总结:
1.线程的任务一旦执行完毕,生命周期就结束,无法再使用(没有使用runloop保活) 2.保住线程的命为什么要用runloop,用强指针不就好了么? 准确来讲,使用runloop是为了让线程保持激活状态 主线程几乎所有的事情都是交给了runloop去做,比如UI界面的刷新、点击时间的处理、performSelector等等
友情链接:
网友评论