美文网首页
RunLoop与Timer以及常用Mode

RunLoop与Timer以及常用Mode

作者: 飘金 | 来源:发表于2017-06-10 10:31 被阅读0次

    创建一个新的项目就不说了,在主控制器上添加一个可以滑动的UI控件,这边我选择UITextView

    直接上代码,代码已经注释的很清楚了

    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        _finished = NO;
        /*创建一个定时器(以这种方式创建的定时器会被自动添加到当前的RunLoop中并且自动开始计时,很显然当前的RunLoop的Mode是NSDefaultRunLoopMode)*/
        //scheduledTimerWithTimeInterval开头设置的定时器都会自动添加到当前RunLoop的NSDefaultRunLoopMode下
        NSTimer *timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeMethod1) userInfo:nil repeats:YES];
        //在界面上添加一个UITextView,里面设置足够多的文本以让它可以滑动,当我们滑动文本框的时候当前的的RunLoop的Mode会切换到UITrackingRunLoopMode用于处理UI的滚动等时间,这时timer因为是设置在NSDefaultRunLoopMode下才计时,故此时timer会暂停计时,知道处理完UI滚动等事件RunLoop再次切换回NSDefaultRunLoopMode这时timer才会继续计时.
        //NSRunLoopCommonModes表示的是NSDefaultRunLoopMode或UITrackingRunLoopMode,也就是说如果把timer设置在NSRunLoopCommonModes下,当RunLoop的Mode为NSDefaultRunLoopMode或UITrackingRunLoopMode计时器都可以正常计时
        
        //创建一个定时器,与上面定时器不同的是该定时器并不会自动添加到当前的RunLoop下,并且不会自动开始计时(可以运行一下,就知道timeMethod2)
        NSTimer *timer2 = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeMethod2) userInfo:nil repeats:YES];
        //如果我们手动开始计时,你回发现@"timeMethod2"只被打印一次就停止了,那时因为timer2并没有被添加到任何的RunLoop中
        [timer2 fire];
        
        //在创建一个定时器,与timer2不同的是我们把timer3手动添加到当前NSRunLoop中,此时计时器就会自动开始计时无需手动调用[timer3 fire]
        NSTimer *timer3 = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeMethod3) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer3 forMode:NSDefaultRunLoopMode];
        [timer3 fire];//此时计时器就会自动开始计时无需手动调用
        
        //上面timer1与timer3都是添加到当前的RunLoop中去,并且是在NSDefaultRunLoopMode下,此时只要滑动界面的UI,这两个定时器timer1与timer3都会暂停计时,为了让计时器在UI滚动时也可以正常计时,我们试着把计时器添加在当前RunLoopMode的UITrackingRunLoopMode模式下
        NSTimer *timer4 = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeMethod4) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer4 forMode:UITrackingRunLoopMode];
        //此时UI滚动时也timer4正常计时,现在在timeMethod4添加[NSThread sleepForTimeInterval:1.0];来模拟处理耗时操作,运行,在滑动UI你会发现虽然计时可以正常计时,但是明显感到了UI滚动时的卡顿,那时因为你在UITrackingRunLoopMode模式下处理了耗时的操作,此时的RunLoop纪要处理UI滚动也处理耗时操作自然会卡顿,显然不妥
        
        //解决timer4的问题,创建一个定时器,并且把他放到子线程中去,子线程我们这边使用继承自NSThread的自定义线程PJThread
        PJThread *pjThread1 = [[PJThread alloc] initWithBlock:^{
            NSTimer *timer5 = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeMethod5) userInfo:nil repeats:YES];
        }];
        //线程跑起来
        [pjThread1 start];
        //pjThread1执行后我们并没有看到"timeMethod5"被打印,并且打印出"PJThread 被销毁了!",说明pjThread1执行完就被销毁了,而timer5没有计时打印出"timeMethod5"是因为他没有被添加到任何RunLoop下
        
        //解决pjThread1与timer5的问题,我们在创建pjThread2与timer6
        PJThread *pjThread2 = [[PJThread alloc] initWithBlock:^{
            NSTimer *timer6 = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeMethod6) userInfo:nil repeats:YES];
            //[NSRunLoop currentRunLoop]会获取当前的RunLoop,**如果当前没有则会创建一个**
            NSRunLoop *runLoop1 = [NSRunLoop currentRunLoop];
            //在NSRunLoopCommonModes下也是可以的,但是苹果官方推荐NSDefaultRunLoopMode,在runLoop1下已经不会受到UI滚动的影响了
            [runLoop1 addTimer:timer6 forMode:NSDefaultRunLoopMode];
            //run之后代码会一直死循环在此处不会往下执行,所以"runLoop1执行之后!"是不会被打印的
            [runLoop1 run];
            NSLog(@"runLoop1执行之后!");
        }];
        [pjThread2 setName:@"pjThread2"];
        //线程跑起来
        [pjThread2 start];
        //此时timer6可以正常计时了,并且pjThread2也没有被销毁了,滑动UI也不会卡顿了,timer6在UI滚动时也可以正常计时,处理耗时操作,这是因为[runLoop1 run]之后代码会一直死循环在此处不会往下执行,所以"runLoop1执行之后!"是不会被打印的,并且pjThread2也不会被销毁
        
        __weak typeof(self) weakSelf = self;
        PJThread *pjThread3 = [[PJThread alloc] initWithBlock:^{
            NSTimer *timer7 = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeMethod7) userInfo:nil repeats:YES];
            //[NSRunLoop currentRunLoop]会获取当前的RunLoop,**如果当前没有则会创建一个**
            NSRunLoop *runLoop2 = [NSRunLoop currentRunLoop];
            //在NSRunLoopCommonModes下也是可以的,但是苹果官方推荐NSDefaultRunLoopMode,在runLoop1下已经不会受到UI滚动的影响了
            [runLoop2 addTimer:timer7 forMode:NSDefaultRunLoopMode];
            //runUntilDate表示每隔多久执行一次runLoop,我们设置点击屏幕就让finished为真YES,看能不能达到退出循环打印"runLoop2执行之后!"(RunLoop执行速度很快,故设置0.01够了),实际运行时可以打印"runLoop2执行之后!",并且也打印"PJThread : pjThread3 被销毁了!"
            while(!weakSelf.finished){
                [runLoop2 runUntilDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0.01]];
            }
            NSLog(@"runLoop2执行之后!");
        }];
        [pjThread3 setName:@"pjThread3"];
        //线程跑起来
        [pjThread3 start];
        
        //到此暂时结束,线程和RunLoop的关系是非常密切的,一个线程要保持一致运行部退出需要RunLoop来帮助实现
    }
    
    - (void)timeMethod1{
        NSLog(@"timeMethod1");
    }
    
    - (void)timeMethod2{
        NSLog(@"timeMethod2");
    }
    
    - (void)timeMethod3{
        NSLog(@"timeMethod3");
    }
    
    - (void)timeMethod4{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"timeMethod4");
    }
    
    - (void)timeMethod5{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"timeMethod5");
    }
    
    - (void)timeMethod6{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"timeMethod6");
    }
    
    - (void)timeMethod7{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"timeMethod7");
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        self.finished = YES;
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:RunLoop与Timer以及常用Mode

          本文链接:https://www.haomeiwen.com/subject/lzpqqxtx.html