skip to Main Content

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:

  1. When the crash occurs, optionalWorld, b, and s are all properly defined.
  2. I originally had the call to filter located inside the DispatchQueue, but it was causing a crash that seemed identical to this one. Moving that line outside the DispatchQueue 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


  1. Chosen as BEST ANSWER

    I did two things, here:

    1. I added Accelerometer and Gyroscope to the UIRequiredDeviceCapabilities key in my Info.plist file. I did this because my game uses Core Motion, but I had neglected to include the necessary values.
    2. On a hunch, I replaced the SCNSceneRendererDelegate method renderer(_: SCNSceneRenderer, updateAtTime: TimeInterval) with the alternative method renderer(_: SCNSceneRenderer, didRenderScene: SCNScene, atTime: TimeInterval).

    Since doing these things, I haven't been able to reproduce a crash.


  2. An alternative unsafe fix is

    try! await Task.sleep(nanoseconds: 1)
    

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search