【问题标题】:Why does the UI freeze when dismissing ViewController with ARSCNView?为什么使用 ARSCNView 关闭 ViewController 时 UI 会冻结?
【发布时间】:2019-02-03 19:12:51
【问题描述】:

我正在运行一个放置 SceneKit 节点的 ARKit 会话。对于每个节点,我都在实例化一个新的视图控制器,并将其视图作为节点的内容传递,如下所示:

func createTextNode(anchor: ARCardAnchor) -> SCNNode? {
    let plane = SCNPlane()
    plane.height = 0.5
    plane.width = 0.5

    let sb = UIStoryboard(name: "Main", bundle: nil)
    let fCVC = sb.instantiateViewController(withIdentifier: "CardViewController") as! CardViewController


    plane.firstMaterial?.diffuse.contents = fCVC.view
    let cardNode = SCNNode(geometry: plane)
    cardNode.constraints = [billboardConstraint]
    return cardNode
}

我正在使用以下 ARSCNViewDelegate 方法和我的自定义 ARCardAnchor(ARAnchor 的子类)将节点添加到场景中:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    if let fcanchor = anchor as? ARCardAnchor {
        DispatchQueue.main.async {
            guard let n = self.nodeCreator.createTextNode(anchor: fcanchor) else { return }
            node.addChildNode(n)

        }
    }
}

到目前为止一切正常,节点被放置在 3D 空间中。但是当我导航回之前的 View Controller 时,UI 冻结了,我什么也做不了。 我试过使用这样的放松转场

@IBAction func goBackToPrevious(_ sender: Any) {
    sceneView.session.pause()
    self.performSegue(withIdentifier: "unwindToPrevious", sender: self)
}

还有一个导航控制器,我将 AR 场景控制器从堆栈中弹出。每次之前的视图控制器都被冻结。 Xcode 中没有错误,应用程序一直在运行。 如果我等待大约。 2分钟我可以再次使用屏幕。如果我不将带有视图控制器的节点添加到我的 AR 场景中,那么一切正常。我唯一的解释是 UIThread 在添加节点时不堪重负,因为它在某处造成了大量的内存泄漏(尽管经过 10 个小时的调试,我还没有发现)。有没有人有类似的经历,能告诉我如何解决这个问题吗?我可以做些什么来调试它并确保顺利导航?

【问题讨论】:

  • 您尝试过使用 Instruments 吗?分配/泄漏在这里会有所帮助。
  • 是的,我使用了主线程检查器以及 Malloc Stack 日志记录。我只是看到主线程检查器告诉我我正在从后台线程调用 UI。所有其他 ARKit 应用程序也会出现这种情况,不会导致同样的问题。所以这似乎是苹果方面的一个问题。

标签: swift memory-leaks arkit freeze ui-thread


【解决方案1】:

问题是我将自定义 UIViewController 视图直接分配给节点的平面。我的假设是这创建了引用循环,因为视图控制器持有对我在场景中其他地方使用的对象的引用。我通过捕获视图的图像并将其分配给节点来解决它。 似乎将 UIViews 直接分配给节点通常是一种危险的做法,因为它会引起引用循环。 也许在内存问题和/或 SceneKit 方面更有资格的人可以对此发表意见。

【讨论】:

    【解决方案2】:
     /* I think whenever UIView are directly attached to SCNMaterial and 
     that SCNMaterial to SCNNode. SCNMaterial are still being used when 
     previous screen is visited. By resetting ARSCNView seems to resolve   
     the issue. */
    
     //try this 
     override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        //reset ARSCNView before going back to previous screen
        self.sceneView = ARSCNView()
     }
    

    【讨论】:

    • 您能解释一下您的代码如何解决 OP 的问题吗?这真的会改善你的答案。
    • @Aimery 我认为每当 UIView 直接附加到 SCNMaterial 并将 SCNMaterial 附加到 SCNNode 时。访问前一个屏幕时仍在使用 SCNMaterial。通过重置 ARSCNView 似乎可以解决问题。
    • 太棒了!只需编辑您的答案以包括解释:)
    • @Aimery 好的。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2021-04-25
    • 2020-01-05
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多