【问题标题】:Heavy SceneKit scene in UINavigationViewController takes too long to deallocUINavigationViewController 中的大量 SceneKit 场景需要很长时间才能解除分配
【发布时间】:2017-09-19 01:58:19
【问题描述】:

我的应用使用标准 UINavigationViewController。

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

这个场景的内存非常重,因为它有大约 6000 个几何图形,每个几何图形都有自己的材质(必须这样)。

当用户单击顶部栏的后退按钮以返回上一个 viewController 时,应用会正确弹出当前的 View Controller 并显示如下。

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

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

-[NSConcreteMapTable countByEnumeratingWithState:objects:count:]

-[SCNMetalResourceManager _geometryWillDie:]

-[SCNMetalResourceManager _materialWillDie:]

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

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

【问题讨论】:

    标签: ios uinavigationcontroller scenekit


    【解决方案1】:

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

    主线程的阻塞可能与 UINavigationController 动画化内容有关。我在尝试为 SCNViews/SCNScenes 设置动画时遇到了问题,而且 SceneKit 和 CoreAnimation 似乎不能很好地协作。

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

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

    2. 您可以尝试在视图控制器中保持对重型场景的强引用准备有问题的视图控制器与重型场景。

      然后,当后者弹出并返回到它下面的那个时,弹出的 View Controller 及其所有内容都应该已解除分配,除了沉重的场景,您将获得对它的引用。

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

      如果没有,可以先删除所有heavyScene.rootNode子节点、action等,然后再deallocate。

      如果这仍然不起作用,您可以先尝试使用递归函数逐个遍历所有节点并先将其几何体/材质归零,然后再释放场景本身。

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

      您将必须检测用户何时按下 UINavigationController“后退按钮”并且带有繁重场景的 VC 被弹出。然后结束,比如 20 帧(60 fps 时为 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
        }
    }
    

    【讨论】:

    • 感谢您的回答。我尝试了选项 1,结果是 4 秒 UI 冻结发生在弹出动画之前而不是之后。所以它似乎与内容的 UIAnimationController 动画无关。我还尝试在不离开当前 ViewController 的情况下将繁重的场景设置为新的空场景,并且 UI 也被阻塞。所以我猜想在主线程上发生了某种解除分配。另外,我尝试使用“enumerateObjectsUsingBlock”来清除所有节点的几何图形,这也阻止了 UI。
    • 您是否尝试过其他选项 - 保留对繁重场景的引用,以便在转到上一个 View Controller 时不会立即释放它,并且只有在转换完成后,将其设置为在后台线程中为零。我还添加了一个新的更实用的解决方案,请参阅上面答案中的 选项 3
    • 嗨@Sulevus,是的,在之前的视图控制器中保留对繁重场景的引用就可以了。非常感谢您的回答!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 2017-01-30
    • 2013-05-02
    • 2013-10-11
    相关资源
    最近更新 更多