问题一(在主线程中执行)
先看一段代码
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil afterDelay:1];
NSLog(@"3");
;
});
}
- (void)printLog {
NSLog(@"2");
}
这段代码的 log 是什么呢,我想大家都知道了
2018-05-27 21:14:38.753695+0800 aaa[44666:6677653] 1
2018-05-27 21:14:38.753910+0800 aaa[44666:6677653] 3
2018-05-27 21:14:39.754311+0800 aaa[44666:6677653] 2
那么这样呢,
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil afterDelay:0];
NSLog(@"3");
;
});
}
- (void)printLog {
NSLog(@"2");
}
我想很多人可能会说 123,因为延迟写的是0去,而结果呢。
2018-05-27 21:15:42.140890+0800 aaa[44727:6679619] 1
2018-05-27 21:15:42.141121+0800 aaa[44727:6679619] 3
2018-05-27 21:15:42.142397+0800 aaa[44727:6679619] 2
在看一个
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil afterDelay:0];
sleep(2);
NSLog(@"3");
;
});
}
- (void)printLog {
NSLog(@"2");
}
这次的结果又是什么呢,我想可能也有好多人会说123,那么答案呢
2018-05-27 21:16:33.261381+0800 aaa[44775:6681215] 1
2018-05-27 21:16:35.261782+0800 aaa[44775:6681215] 3
2018-05-27 21:16:35.263039+0800 aaa[44775:6681215] 2
由此可见 这个方法异步执行的,不会阻塞当前的线程,会等待主线程的任务执行完成之后才回去执行
而换成这样呢
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil ];
sleep(2);
NSLog(@"3");
;
});
}
- (void)printLog {
NSLog(@"2");
}
答案是123,可见这个方法是同步=执行的会阻塞当前的线程。
问题二(在子线程中执行)
再看一段代码,将主线程改为子线程
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil ];
sleep(2);
NSLog(@"3");
;
});
}
- (void)printLog {
NSLog(@"2");
}
可见结果是123,证明这个方法是同步执行的,与线程无关
在看一个
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil afterDelay:0];
NSLog(@"3");
;
});
}
- (void)printLog {
NSLog(@"2");
}
答案是什么呢,说到答案,有很多人肯定就蒙了,
2018-05-27 21:21:31.217771+0800 aaa[45008:6689138] 1
2018-05-27 21:21:31.218179+0800 aaa[45008:6689138] 3
方法没有执行,为什么呢,performSelector 是需要提交到runloop上面去执行的,GCD底层分派的线程是没有开启runloop能力的,也就是说在子线程中,runloop默认是没有开启的,说到 nstimer 大家就一定会明白,我们在自现场建立一个nstimer,是需要run起来的,这个和那个是一样的道理,因为 子线程的runloop没有开启,所以方法没有执行.
改为这样呢
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil afterDelay:2];
[[NSRunLoop currentRunLoop] run];
NSLog(@"3");
;
});
}
- (void)printLog {
NSLog(@"2");
}
答案是
2018-05-27 21:26:25.654391+0800 aaa[45232:6696585] 1
2018-05-27 21:26:27.658696+0800 aaa[45232:6696585] 2
2018-05-27 21:26:27.658891+0800 aaa[45232:6696585] 3
当开启runloop后,会立刻去执行runloop的操作,等待执行完毕后,继续向下执行,runloop的开启位置很重要,决定了,代码的执行顺序,比如我们这样,
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil afterDelay:2];
NSLog(@"3");
;
[[NSRunLoop currentRunLoop] run];
});
}
- (void)printLog {
NSLog(@"2");
}
答案是
2018-05-27 21:28:23.793809+0800 aaa[45328:6699868] 1
2018-05-27 21:28:23.794209+0800 aaa[45328:6699868] 3
2018-05-27 21:28:25.799013+0800 aaa[45328:6699868] 2
开启的是当前子线程的runloop,其余代码征程执行,当开启的时候,会立刻执行runloop的操作,是不是很神奇。
总结
我看到网上有人说,performSelector
只能在主线程上面执行,这种说法是错误的,是可以在子线程中执行的,需要开启当前线程的 runloop
,有可能我上面的解释不对,如果有不对的,一定要给我评论,纠正我,共同进步,谢谢!