DispatchGroup出现EXC_BAD_INSTRUCTION问题

前言

前段时间看app的线上奔溃总是出现意义不明的崩溃问题,而且崩溃栈出现在闭包的回调里,并且bugly上提示说是有可能在swift解包的时候出现问题,也就是对nil使用了!。这就完全误导了我,把我对问题的理解定义为数据保护的不够到位。结果就是在多次发版后这个问题还未得到解决,所以我绝对对这个问题一探究竟。

出现问题的原因

其实出现这个问题的原因很简单,那就是group的enter和leave没有成对出现。比如说我们会在闭包的回调里去leave,但是闭包有可能返回多次,一旦多leave了,那么就会出现EXC_BAD_INSTRUCTION这个问题。

举个例子

我们声明如下两个方法,可以看出来work2回调了两次

1
2
3
4
5
6
7
8
9
10
func work1(closure: () -> Void) {
sleep(1)
closure()
}

func work2(closure: () -> Void) {
closure()
sleep(2)
closure()
}

接着我们使用它们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
override func viewDidLoad() {
super.viewDidLoad()

let group = DispatchGroup()
let queue = DispatchQueue(label: "test", qos: .background, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)

group.enter()
queue.async {
self.work1 {
group.leave()
}
}

group.enter()
queue.async {
self.work2 {
group.leave()
}
}

group.notify(queue: queue) {
print("同步完成")
}
}

这里可以看出来在work2的闭包的leave就会出现这个问题。

解决方法

其实这种问题完全使我们编写代码的过程中不够细心导致的,通过完善的测试用例完全可以避免问题发生,但是我们还是需要记住一句话DispatchGroup的enter和leave必须成对出现!!!