In a SceneKit project, the following method is intermittently (but consistently) crashing with EXC_BAD_ACCESS. Specifically, it says Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
.
contactTestBetween(_:_:options:)
The method is called from inside SceneKit’s SCNSceneRendererDelegate
method. It’s also being run on the main thread because otherwise, this code crashes even more often. So, here’s the greater context:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
var ball = self.scene?.rootNode.childNode(withName: "ball", recursively: true)
var ballToFloorContact: [SCNPhysicsContact]?
let theNodes: [SCNNode]? = self.scene?.rootNode.childNodes.filter({ $0.name?.contains("floor") == true})
let optionalWorld: SCNPhysicsWorld? = self.scene?.physicsWorld
DispatchQueue.main.async {
if let allNodes = theNodes {
for i in 0..<allNodes.count {
let n = allNodes[i]
if let b = n.physicsBody, let s = ball?.physicsBody {
ballToFloorContact = optionalWorld?.contactTestBetween(b, s)
}
}
}
}
}
The SCNSceneRendererDelegate
is set in viewDidLoad
:
scnView.delegate = scnView
Additional info:
- When the crash occurs,
optionalWorld
,b
, ands
are all properly defined. - I originally had the call to
filter
located inside theDispatchQueue
, but it was causing a crash that seemed identical to this one. Moving that line outside theDispatchQueue
solved that problem.
Question: Any idea what might be causing this crash, and how I might avoid it? Am I doing something wrong, here?
Thanks!
UPDATE: I tried adding the following guard
statement to protect against a situation where the contactTestBetween
method is, itself, nil (after all, that seems to be what Xcode is telling me):
guard let optionalContactMethod = optionalWorld?.contactTestBetween else {
return
}
However, after some additional testing time, contactTestBetween
eventually crashed once again with EXC_BAD_ACCESS
on the line guard let optionalContactMethod = optionalWorld?.contactTestBetween else {
. I truly do not understand how that could be, but it be. Note that I tried this guard
paradigm both with and without the presence of the DispatchQueue.main.async
call, with the same result.
2
Answers
I did two things, here:
Accelerometer
andGyroscope
to theUIRequiredDeviceCapabilities
key in myInfo.plist
file. I did this because my game usesCore Motion
, but I had neglected to include the necessary values.SCNSceneRendererDelegate
methodrenderer(_: SCNSceneRenderer, updateAtTime: TimeInterval)
with the alternative methodrenderer(_: SCNSceneRenderer, didRenderScene: SCNScene, atTime: TimeInterval)
.Since doing these things, I haven't been able to reproduce a crash.
An alternative unsafe fix is
somewhere before invoking
contactTestBetween()
.This answer sits here as a warning to anyone who might want to use async/await as a fix or is inadvertendly using some await (any kind of await works) to break out of the run loop which somehow makes debugging
EXC_BAD_ACCESS
nearly impossible because it will get triggered rarely enough.I know from experience that if the above fixed a race then races WILL still happen especially and mostly with CPUs running at 100% and actually I got
contactTestBetween
to crash once after many tries.I have no clue what’s going on behind the scenes or how to synchronize with
physicsWorld
.@West1 solution is still the best if it works as advertised.