iOS中使用dispatch_sync崩溃问题。总结

崩溃发现是这样的,在维护项目过程中,有个地图的功能,在地图上会添加很多的标注物,但是我在测试过程中发现只要快速操作,比如连续点击放大地图,就会出现崩溃现象。

崩溃点在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] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.432 测试Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.434 测试Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.434 测试Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.434 测试Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}
2017-08-28 16:28:00.435 测试Demo[68568:1922504] <NSThread: 0x608000076b00>{number = 1, name = main}

显示的线程都是在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
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值