RunLoop与Timer以及常用Mode

RunLoop与Timer以及常用Mode

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
@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
------本文结束😘感谢阅读------