UINavigationViewController 中的重 SceneKit 场景需要很长时间才能解除分配

Heavy SceneKit scene in UINavigationViewController takes too long to dealloc

我的应用使用标准的UI导航ViewController。

在某些时候,用户已导航到显示 SceneKit 场景的 ViewController。

这个场景内存很重,因为它有大约 6000 个几何体,每个几何体都有自己的Material(需要这样)。

当用户单击顶部栏的后退按钮以 return 到上一个 viewController 时,应用会正确弹出当前视图控制器并显示下面的视图控制器。

但是,大约 4 秒后,显示的新 ViewController 的 UI 冻结。

我使用了 Instruments Time Profiler,我可以看到这 4 秒的大部分时间都是由这些 SceneKit 方法占用的:

-[NSConcreteMapTablecountByEnumeratingWithState:objects:count:]

-[SCNMetalResourceManager _geometryWillDie:]

-[SCNMetalResourceManager _materialWillDie:]

这是有道理的,因为有这么多的几何形状和材料。

我该如何解决这种情况,以便在释放繁重的场景时 UI 不被阻塞,无论是从 SceneKit 的角度(使释放更快)还是从 UINavigationViewController 透视图(也许强制场景的重新分配发生在单独的线程中?)。

我的理解是 SceneKit 使用它自己的后台线程来工作,所以它不应该阻塞主线程。

主线程的阻塞可能与 UINavigationController 动画内容有关。我在尝试制作动画时遇到了问题 SCNViews/SCNScenes,看起来 SceneKit 和 CoreAnimation 不能很好地协作。

我这里没有完整的上下文,但我可以建议几件事供您测试:

  1. 你可以尝试将重场景scnView.scene = [SCNScene scene];的SCNView的场景属性设置为一个新的空场景,就在有场景的View Controller被弹出之前,这样沉重的场景就不会参与动画,SceneKit 会在它的背景威胁中正确地释放它。

  2. 您可以尝试在 View Controller 中保持对重场景的强引用,以准备 有问题的 View Controller 与重场景。

    然后当后者弹出时你return到它下面的那个,弹出的视图控制器和它的所有内容应该已经释放,除了沉重的场景,你会有一个参考

    在这里你可以尝试设置为 nil(在动画完成后),它可能会在 SceneKit 后台线程上正确释放。

    如果没有,您可以先移除所有heavyScene.rootNode个子节点、动作等,然后再释放。

    如果还是不行,你可以先尝试使用递归函数一个一个遍历你所有的节点,先将它们geometry/material置零,然后再释放场景本身。

  3. 使用渲染循环-renderer:updateAtTime:方法在几帧(不到一秒)内移除节点。然后你可以确定 SceneKit 会按照它设计的方式处理它们。

    您将必须检测用户何时按下 UINavigationController "Back button" 并且带有沉重场景的 VC 被弹出。然后,假设 20 帧(60fps 时为 1/3 秒),在每帧移除 5% 的节点,因此仍然会有短暂的延迟,但 1/3 秒,希望不会引起注意。

    可以通过覆盖其 didMoveToParentViewController: 方法并检查 nil 是否作为 parent 参数传递来检测 VC 被弹出。

例如

- (void)didMoveToParentViewController:(UIViewController *)parent {

    if (!parent) {
        // work that should be done when VC is being removed from parent
        // maybe set a counter in the VC being popped off to
        // count down from 20 to 0, -1 at each -update call
    }
}