崩溃发现是这样的,在维护项目过程中,有个地图的功能,在地图上会添加很多的标注物,但是我在测试过程中发现只要快速操作,比如连续点击放大地图,就会出现崩溃现象。
崩溃点在dispatch_sync上面,使用同步县城
dispatch_sync(dispatch_get_main_queue(), ^{
@try {
[self.mapView removeAnnotations:removeArray];
[self.mapView addAnnotations:addArray];
} @catch (NSException *exception) {
NSLog(@"%@", exception);
}
}
后来修改成了
dispatch_async(dispatch_get_main_queue(), ^{
@try {
[self.mapView removeAnnotations:removeArray];
[self.mapView addAnnotations:addArray];
} @catch (NSException *exception) {
NSLog(@"%@", exception);
}
}
恰好在查找过程中看到一道面试题,和我这个崩溃点是一样的。
请看下面这道面试题,输出结果会是什么了?
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
这道面试题,是在主线程中先进行同步操作打印@"2",然后异步操作打印@"3",那么输出结果是什么呢。
答案是和我之前遇到的问题一样崩溃在了同步操作上。
出现这个问题的主要原因是,在同步线程里面执行dispatch_get_main_queue()时会发送线程卡死的现象。
我们都是在主队列中去更新UI,即我们的界面都是由系统在主队列中维护的,我们在viewDidLoad方法中使用如下GCD方法
dispatch_sync(dispatch_get_main_queue(), ^{
});
就会出错,我们知道同步线程不会去创建新的线程。 验证一下。
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i <10; i++)
{
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@" %@", [NSThread currentThread]);
});
}
}
输出结果
2017-08-28 16:28:00.431 测试Demo[68568:1922504]
2017-08-28 16:28:00.432 测试Demo[68568:1922504]
2017-08-28 16:28:00.434 测试Demo[68568:1922504]
2017-08-28 16:28:00.434 测试Demo[68568:1922504]
2017-08-28 16:28:00.434 测试Demo[68568:1922504]
2017-08-28 16:28:00.435 测试Demo[68568:1922504]
显示的线程都是在main主队列下。
所以我们是和UI在同一个线程里面,并且再次去执行一个主队列任务。我们知道dispatch_get_main_queue()这个主队列是一个串行的队列,而串行队列的基础法则时FIFO(先进先出)。所以这时我们就找到了卡死的问题点了:系统维护的dispatch_get_main_queue()这个队列里面在执行viewDidLoad方法,在viewDidLoad中又再次在dispatch_get_main_queue()这个相同的队列里面执行block方法。由于串行队列FIFO原则,系统维护的dispatch_get_main_queue()先进栈,所以要先执行完毕后,再执行后进栈的队列任务,而系统维护的dispatch_get_main_queue()执行完的条件时viewDidLoad方法执行完毕。
所以系统维护的dispatch_get_main_queue()会等待dispatch_sync调用的dispatch_get_main_queue()执行完毕,dispatch_sync调用的ispatch_get_main_queue()又在等待先进栈的系统维护的dispatch_get_main_queue()执行完毕,这样就陷入死循环。
所以我们得出一个结论:在同一个线程里面,调用同一个串行队列会发送死锁现象。
作者:Hardy_Hu
链接:https://www.jianshu.com/p/c39c1db05305
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。