The crash
If you find your code crashing (EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)) in the deinit function of a class you made. Even if you didn’t write a deinit-function yourself, but get a crash in the deinit automatically generated by the compiler. And if you in that class has a property with an instance of a DispatchQueue (but it could be any of the subtypes of DispatchObject, really).
Chances are you fell into the same trap as I did, and had to spend quite some time to figure out what caused it.
Example
Given the following example code, where a class has a property which it creates and assigns a DispatchQueue to:
import Foundation
class Client {
init() {
dq = DispatchQueue(label: UUID().uuidString, attributes [.concurrent, .initiallyInactive])
}
private let dq : DispatchQueue
deinit() {
print("crashes when leaving this scope")
}
}
When your run the following bit of code (in a test for example):
var client : Client? = Client()
client = nil // this makes the deinit to execute and crash
it will crash in the deinit after printing the message: crashes when leaving this scope.
The problem
In his WWDC talk from 2016, Pierre mentions the criteria for deallocation of dispatch objects and its sub types, like DispatchQueue. The two conditions the DispatchQueue must meet are: it must be active, and it must not be suspended.
In the example above, the queue is created inactive, which means that it cannot directly be deallocated without activating the queue. Hence, the deinit cannot complete successfully, and crashes.
deinit() {
dq.activate()
}
Other lessons learned
Last, but not least, I also learnt that I cannot take for granted that deinit of my classes always works and that its prudent to test that newly initialized object can be deinit:ed without problems, atleast, when dealing with DispatchObjects.