美文网首页
iOS EXC_BAD_INSTRUCTION类型 奔溃

iOS EXC_BAD_INSTRUCTION类型 奔溃

作者: 路飞_Luck | 来源:发表于2018-07-12 09:56 被阅读105次
    序言

    在做项目的时候,遇到一个巨坑,在使用dispatch_group 的时候遇到奔溃,此问题较难定位,但是解决方法较为简单,详细如下所示。

    crash 场景
    1. 有一组多个接口地址
    2. 利用dispatch_group 并发请求到数据后,统一回调(必须利用 AFN进行网络请求)
    3. 频繁调用(必要)
    4. 每次调用2中的请求是同一个接口地址(必要)
    备注:频繁的意思是一秒调用3次或3次以上
    

    问题核心:

    对dispatch_group 进行了额外的 leave 操作
    

    问题代码

    - (void)errorRequest {
        dispatch_group_t group = dispatch_group_create();
        self.group = group;
        
        // enter code
        [request  requestGetUrl:url success:^(id responds) {
            // leave code
        }];
    }
    

    修正后代码

    - (void)rightRequest {
        if (self.group == nil) {
            dispatch_group_t group = dispatch_group_create();
            self.group = group;
        }
        
        // enter code
        [request  requestGetUrl:url success:^(id responds) {
            // leave code
        }];
    }
    

    产生此问题的原因:概况的说是dispatch_group 的原理和 AFNetworking 网络请求回调 block 的缓存回调原理的协作问题。

    举例详细说明,流程图如下:

    image.png
    1. 正常情况下:执行步骤1的问题间隔时间充分长,或者只执行一次,此逻辑没有问题,不会 crash。
    2. 非正常情况下:开头所说的 crash 出现场景下,即频繁执行上图中步骤1至步骤3.
    

    因为网络请求的耗时和异步特性,有时候会发现一些情况

    1. 第一次在步骤1创建了一个新的 group1,这时网络请求 n1到 n4请求未返回,也就是b1到 b4还未返回。
    2. 此时又执行了一次步骤一创建了一个新的group,命名为 group2(这是一个新的),并且赋值给了self.group,又执行步骤二,对 group2进行 enter,发送网络请求。
    3. 此时若是1中的网络请求返回了,b1到 b4就会调用,会对步骤2中创建的新的 group2进行 leave 操作,当2中的网络请求返回,进行回调时,会发生对 group2进行额外的 leave 操作,从而造成 crash。
    注:b1到 b4因为 leave 的需要,会对 group 进行地址引用。
    

    模拟网络请求奔溃代码
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        dispatch_group_t group = dispatch_group_create();
        self.group = group;
        
        dispatch_group_enter(self.group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_group_leave(self.group);
        });
        
        dispatch_group_enter(self.group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_group_leave(self.group);
        });
    
        dispatch_group_enter(self.group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_group_leave(self.group);
        });
    
        dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{
            NSLog(@"操作全部完成");
        });
    }
    

    多次点击屏幕后发生crash

    image.png
    修正后网络请求代码
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        if (self.group == nil) {
            dispatch_group_t group = dispatch_group_create();
            self.group = group;
        }
        
        dispatch_group_enter(self.group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_group_leave(self.group);
        });
        
        dispatch_group_enter(self.group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_group_leave(self.group);
        });
    
        dispatch_group_enter(self.group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_group_leave(self.group);
        });
    
        dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{
            NSLog(@"操作全部完成");
        });
    }
    

    运行结果

    image.png

    没有发生奔溃


    本文参考iOS开发--Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) 的一类解决办法,非常感谢该作者。

    iOS疑难问题排查之深入探究dispatch_group crash,这篇博客介绍的非常详细,有深度。


    项目连接地址

    相关文章

      网友评论

          本文标题:iOS EXC_BAD_INSTRUCTION类型 奔溃

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