RunLoop(二)

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_06.png

  • 苹果用 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 
    @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 *)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] - {number = 3, name = (null)}
    Runloop[2520:239171] -[YDThread1 dealloc]
    Runloop[2520:239175] -[TestVC01 run] - {number = 4, name = (null)}
    Runloop[2520:239175] -[YDThread1 dealloc]
    Runloop[2520:239205] -[TestVC01 run] - {number = 5, name = (null)}
    Runloop[2520:239205] -[YDThread1 dealloc]
    
    说明:
    每次点击总会创建一个子线程来处理事件(执行完会自动释放)
    这样会很消耗资源
    

    Test02:

    YDThread2.h
    #import 
    @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 *)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] - {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 
    @interface YDThread3 : NSThread
    @end
    
    YDThread3.m
    #import "YDThread3.h"
    @implementation YDThread3
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC03.h
    #import 
    @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 *)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 
    @interface YDThread4 : NSThread
    @end
    
    YDThread4.m
    #import "YDThread4.h"
    @implementation YDThread4
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC04.h
    #import 
    @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 *)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 
    @interface YDThread5 : NSThread
    @end
    
    YDThread5.m
    #import "YDThread5.h"
    @implementation YDThread5
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    TestVC05.h
    #import 
    @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 *)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 
    @interface YDThread6 : NSThread
    @end
    
    YDThread6.m
    #import "YDThread6.h"
    @implementation YDThread6
    -(void)dealloc{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    
    TestVC06.h
    #import 
    @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 *)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 
    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 
    @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 *)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等等
    

    友情链接:

  • Runloop(一)

  • gitHub_Demo

你可能感兴趣的:(RunLoop(二))