Reputation: 352
Here is the error I am getting, check the attached picture for more info.
com.apple.scenekit.scnview-renderer (17): EXC_BAD_ACCESS (code=1, address=0xf000000010a10c10)
I can reproduce this error when I call the following function but only if this function gets called many times within a second. This would happen If the user rapidly taps the button to cycle to the next available car.
As you can see, I tried wrapping this in a DispatchQueue
to solve my problem.
You'll also notice that I created a Bool alreadyCyclingCars
to track whether the cycleCarNext()
function is finished before allowing it to be called again.
This function essentially iterates through all the unlockedCars
in the unlockedCars
array.
If the type of car matches the one we are currently looking for, I break the loop.
Then we determine the index of the current car to see whether the next car I need to show is the next one in the array (if there is one) if not, we have arrived at the end of the array so I show the first car in the array.
Does anyone know more than I do about this? Would really be appreciated thank you!
func cycleCarNext() {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
if !self.alreadyCyclingCars {
self.alreadyCyclingCars = true
var indexOfCurrentCar = 0
for (index, car) in UserData.shared.unlockedCars.enumerated() {
if car.type == self.overlayScene.currentCarOnDisplay {
indexOfCurrentCar = index
break
}
}
if indexOfCurrentCar < UserData.shared.unlockedCars.count - 1 {
let nextCar = UserData.shared.unlockedCars[indexOfCurrentCar+1]
self.playerNode.removeFromParentNode()
self.playerNode = nextCar
self.playerNode.name = "player"
self.playerNode.position = SCNVector3(x: 17, y: 0.3, z: 0)
self.playerNode.eulerAngles = SCNVector3(x: 0, y: toRadians(angle: 45),z: 0)
self.scene.rootNode.addChildNode(self.playerNode)
self.overlayScene.currentCarOnDisplay = nextCar.type
self.overlayScene.updateGarageInterface()
} else {
guard let nextCar = UserData.shared.unlockedCars.first else { return }
self.playerNode.removeFromParentNode()
self.playerNode = nextCar
self.playerNode.name = "player"
self.playerNode.position = SCNVector3(x: 17, y: 0.3, z: 0)
self.playerNode.eulerAngles = SCNVector3(x: 0, y: toRadians(angle: 45),z: 0)
self.scene.rootNode.addChildNode(self.playerNode)
self.overlayScene.currentCarOnDisplay = nextCar.type
self.overlayScene.updateGarageInterface()
}
self.alreadyCyclingCars = false
}
}
}
Upvotes: 13
Views: 3811
Reputation: 974
Maybe you didn't init the object and its seems to render before the allocation. You can fix this using the awakeFromNib
method. Like this:
override func awakeFromNib() {
super.awakeFromNib()
createScene()
}
Upvotes: 0
Reputation: 1453
If "GPU Frame Capture" is enabled in your scheme disabling it fixes this issue:
In Xcode go to your current scheme -> select »Edit Scheme…« -> Run/Options: set »GPU Frame Capture« to Disabled.
Taken from here.
Upvotes: 0
Reputation: 231
I've been struggling with this error for a while, and I just wanted to add a note in case it helps anyone using SCNRenderer
, which is that I was using
.render(withViewport:commandBuffer:passDescriptor:)
but this does not call the delegate render method. Instead, use
.render(atTime:viewport:commandBuffer:passDescriptor:)
even if you are not using the time interval parameter, and then the delegate method renderer(_:updateAtTime:)
will be called, where you can make the scene updates safely.
Upvotes: 0
Reputation: 1005
Faced with the same problem and figured out that this crash caused by personSegmentationWithDepth on devices with LiDAR
if ARWorldTrackingConfiguration.supportsFrameSemantics(.personSegmentationWithDepth) {
configuration.frameSemantics.insert(.personSegmentationWithDepth)
}
Upvotes: 0
Reputation: 2897
It's my experience that those kind of errors occur when you attempt to modify SceneKit's scene graph (add/remove nodes, etc) outside the SCNSceneRendererDelegate
delegate methods.
Imagine you have one thread that is performing rendering at 60fps, and another (eg; the main thread) that removes a node from what is to be rendered. At some point the rendering thread is going to be part way through rendering when what it is rendering is removed by the other thread. This is when the EXC_BAD_ACCESS
occurs. The more times the scene is modified, the more likely you are to see this conflict, hence why button mashing could more readily reproduce the issue.
The fix is to only modify your scene in one of SCNSceneRendererDelegate
delegate methods. I'd try something like...
func cycleCarNext() {
self.cycleNextCar = true
}
func renderer(renderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
if (self.cycleNextCar) {
self.doCycleNextCar()
self.cycleNextCar = false
}
}
func doCycleNextCar() {
var indexOfCurrentCar = 0
for (index, car) in UserData.shared.unlockedCars.enumerated() {
if car.type == self.overlayScene.currentCarOnDisplay {
indexOfCurrentCar = index
break
}
}
if indexOfCurrentCar < UserData.shared.unlockedCars.count - 1 {
let nextCar = UserData.shared.unlockedCars[indexOfCurrentCar+1]
self.playerNode.removeFromParentNode()
self.playerNode = nextCar
self.playerNode.name = "player"
self.playerNode.position = SCNVector3(x: 17, y: 0.3, z: 0)
self.playerNode.eulerAngles = SCNVector3(x: 0, y: toRadians(angle: 45),z: 0)
self.scene.rootNode.addChildNode(self.playerNode)
self.overlayScene.currentCarOnDisplay = nextCar.type
self.overlayScene.updateGarageInterface()
} else {
guard let nextCar = UserData.shared.unlockedCars.first else { return }
self.playerNode.removeFromParentNode()
self.playerNode = nextCar
self.playerNode.name = "player"
self.playerNode.position = SCNVector3(x: 17, y: 0.3, z: 0)
self.playerNode.eulerAngles = SCNVector3(x: 0, y: toRadians(angle: 45),z: 0)
self.scene.rootNode.addChildNode(self.playerNode)
self.overlayScene.currentCarOnDisplay = nextCar.type
self.overlayScene.updateGarageInterface()
}
}
cycleCarNext
is to be called by your main thread as it currently is. You'll likely need to set the SCNView's delegate somewhere too (eg; sceneView.delegate = self
)
The idea is that while the cycleCarNext
boolean is set immediately in the main thread, the scene isn't changed. Instead the change occurs at the correct time/thread in the SceneKit rendering loop.
Upvotes: 11