美文网首页Gcd
dispatch_group_leave崩溃

dispatch_group_leave崩溃

作者: 小小棒棒糖 | 来源:发表于2022-05-05 16:56 被阅读0次

    崩溃描述

    image.png
    近日排查线上崩溃时,发现一个描述信息很少的崩溃,如上。由dispatch_group_leave.cold.1可知,属于dispatch_group异常

    dispatch_group使用

    dispatch_group使用场景:A任务依赖B/C/D子任务全部执行完成,才进行触发执行。
    如何添加子任务,通常有两种方式:

    1. block
    2. dispatch_group_enter+dispatch_group_leave
        dispatch_group_t group = dispatch_group_create();
    
        // 添加N个子任务...
        dispatch_group_enter(group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // 耗时任务task1
            dispatch_group_leave(self.group);
        });
        // ...
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"task123完成,收到通知");
        });
    

    为什么dispatch_group_leave崩溃?

    dispatch_group维持有一个计数。

    • dispatch_group_create初始化为LONG_MAX
    • dispatch_group_leave时+1
    • dispatch_group_enter时-1。

    当leave次数超出enter次数时,LONG_MAX+1溢出,触发Crash。

    陷阱规避

    1. 属性记录dispatch_group_t,易引发错位leave

    示例错误代码:

    // 强引用group对象
    @property (nonatomic, strong) dispatch_group_t group;
    @property (nonatomic, strong) NSOperationQueue *queue;
    
    - (void)cancel {
        self.cancelled = YES;
        [self.queue cancelAllOperations];
    }
    
    - (void)exortImage:(NSArray<NSString *> *)infos {
        self.group = dispatch_group_create();
        self.cancelled = NO;
        
        __weak typeof(self) weak_self = self;
        for (NSString *info in infos) {
            dispatch_group_enter(self.group);
            [self.queue addOperationWithBlock:^{
                // 大量计算与io耗时操作
                // ...
                dispatch_group_leave(weak_self.group);
            }];
        }
        dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{
            if (!weak_self.cancelled) {
                // 导出完成,下一步
            }
        });
    }
    
    - (NSOperationQueue *)queue {
        if (!_queue) {
            _queue = [[NSOperationQueue alloc] init];
            _queue.maxConcurrentOperationCount = 2;
        }
        return _queue;
    }
    

    复现步骤:

    假如infos只有一条数据,单个导出任务耗时10s,任务A在进行到5s时,取消任务A,重新触发新的导出B,A/B均完成时,就会崩溃。

    原因分析:

    1. NSOperationQueue已经调度的任务,其实是无法直接取消的,任务仍会继续执行
    2. A任务再经历5s后完成,调用dispatch_group_leave(weak_self.group);,但其实A创建的group已释放,调用的是B的group.leave,计数为LONG_MAX,并触发notify。
    3. 当B也完成任务时,再次调用dispatch_group_leave,导致崩溃。

    相关文章

      网友评论

        本文标题:dispatch_group_leave崩溃

        本文链接:https://www.haomeiwen.com/subject/arxoyrtx.html