【问题标题】:How to solve SceneKit Renderer "EXC_BAD_ACCESS (code=1, address=0xf000000010a10c10)"?如何解决 SceneKit 渲染器“EXC_BAD_ACCESS (code=1, address=0xf000000010a10c10)”?
【发布时间】:2019-04-07 03:32:36
【问题描述】:

这是我遇到的错误,请查看附件图片了解更多信息。

com.apple.scenekit.scnview-renderer (17):EXC_BAD_ACCESS(代码=1,地址=0xf000000010a10c10)

以下是错误日志:

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

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

您还会注意到,我创建了一个 Bool 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
            }
        }
    }

【问题讨论】:

  • @RX9 谢谢先生!

标签: swift scenekit


【解决方案1】:

根据我的经验,当您尝试在 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 渲染循环中的正确时间/线程。

【讨论】:

  • 谢谢你,我真的很感激,特别是因为现在是星期五的午夜!我在实施之前接受了这个答案,但会给你反馈!
  • 效果很好!谢谢先生,我用这个小技巧来更新场景的其他一些功能。
  • 真棒@PatTrudel,很高兴它成功了。肯定会推荐这种方法来对场景进行任何修改。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    我一直在努力解决这个错误,我只是想添加一个注释,以防它帮助任何使用 SCNRenderer 的人,这是我正在使用的

    .render(withViewport:commandBuffer:passDescriptor:)
    

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

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

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

    【讨论】:

      【解决方案4】:

      如果您的方案中启用了“GPU 帧捕获”,则禁用它可以解决此问题:

      在 Xcode 中转到您当前的方案 -> 选择 »编辑方案...« -> 运行/选项:将 »GPU 帧捕获« 设置为禁用。

      取自here

      【讨论】:

        猜你喜欢
        • 2017-12-19
        • 2021-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-21
        • 1970-01-01
        • 2013-11-04
        • 1970-01-01
        相关资源
        最近更新 更多