RunLoop与Timer以及常用Mode 发表于 2016-10-25 | 分类于 iOS RunLoop与Timer以及常用Mode 创建一个新的项目就不说了,在主控制器上添加一个可以滑动的UI控件,这边我选择UITextView直接上代码,代码已经注释的很清楚了123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107@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的自定义线程PJThreadPJThread *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与timer6PJThread *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 ------本文结束😘感谢阅读------