iOS EXC_BAD_INSTRUCTION类型 奔溃
2018-07-12 本文已影响105人
路飞_Luck
序言
在做项目的时候,遇到一个巨坑,在使用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.png1. 正常情况下:执行步骤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,这篇博客介绍的非常详细,有深度。