如何解决SceneKit渲染器“ EXC_BAD_ACCESS(代码= 1,地址= 0xf000000010a10c10)”?

时间:2018-11-02 21:45:56

标签: swift scenekit

这是我遇到的错误,请查看所附图片以获取更多信息。

  

com.apple.scenekit.scnview-renderer(17):EXC_BAD_ACCESS(code = 1,address = 0xf000000010a10c10)

以下是错误的日志: More Info about the error

当我调用以下函数时,但仅当此函数在一秒钟内被多次调用时,才可以重现此错误。如果用户迅速点击该按钮以循环到下一辆可用的汽车,就会发生这种情况。

如您所见,我尝试将其包装在DispatchQueue中以解决我的问题。

您还将注意到,我创建了一个布尔alreadyCyclingCars来跟踪cycleCarNext()函数是否已完成,然后才能再次调用。

此函数本质上遍历unlockedCars数组中的所有unlockedCars

如果汽车的类型与我们目前正在寻找的汽车相匹配,我会打破循环。

然后,我们确定当前汽车的索引,以查看我需要显示的下一辆汽车是否是数组中的下一辆(如果有)(如果没有),我们到达了数组的末尾,因此我显示阵列中的第一辆汽车。

有谁比我对此了解更多? 谢谢您!

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
            }
        }
    }

3 个答案:

答案 0 :(得分:5)

根据我的经验,当您尝试在SCNSceneRendererDelegate委托方法之外修改SceneKit的场景图(添加/删除节点等)时,会发生此类错误。

想象一下,您有一个线程以60fps的速度执行渲染,而另一个线程(例如主线程)从要渲染的对象中删除了一个节点。在某个时候,当渲染线程被另一个线程删除时,渲染线程将成为渲染的一部分。这是EXC_BAD_ACCESS发生的时间。修改场景的次数越多,您越有可能看到这种冲突,因此为什么按钮混搭可以更轻松地重现该问题。

解决方法是仅在SCNSceneRendererDelegate委托方法之一中修改场景。我会尝试类似...

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将由您的主线程当前调用。您可能还需要在某个地方设置SCNView的委托(例如sceneView.delegate = self

这个想法是,尽管在主线程中立即设置了cycleCarNext布尔值,但场景没有改变。相反,更改会在SceneKit渲染循环中的正确时间/线程处进行。

答案 1 :(得分:0)

遇到同样的问题,发现这个崩溃是由personSegmentationWithDepth在带有LiDAR的设备上引起的

if ARWorldTrackingConfiguration.supportsFrameSemantics(.personSegmentationWithDepth) {
    configuration.frameSemantics.insert(.personSegmentationWithDepth)
}

答案 2 :(得分:0)

我一直在为这个错误苦苦挣扎,我只是想添加一个注释,以防它对使用 SCNRenderer 的任何人有所帮助,这是我正在使用的

.render(withViewport:commandBuffer:passDescriptor:)

但这不会调用委托渲染方法。相反,使用

.render(atTime:viewport:commandBuffer:passDescriptor:)

即使你没有使用时间间隔参数,然后委托方法renderer(_:updateAtTime:)也会被调用,在这里你可以安全地进行场景更新。