iOS EXC_BAD_INSTRUCTION类型 奔溃

序言

在做项目的时候,遇到一个巨坑,在使用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,这篇博客介绍的非常详细,有深度。


项目连接地址

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,522评论 8 265
  • OC语言基础 1.类与对象 类方法 OC的类方法只有2种:静态方法和实例方法两种 在OC中,只要方法声明在@int...
    奇异果好补阅读 4,313评论 0 11
  • 1、截取字符串”20 | http://www.baidu.com”中,”|”字符前面和后面的数据,分别输出它们 ...
    强子ly阅读 2,993评论 8 46
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,135评论 1 32
  • 一样的目的,目前网上到处充斥着图片的瘦身,但是能完整记录下来并运用与实践的估计少之又少,本文档是在对我在项目中资源...
    roger_Hunter阅读 988评论 2 16