我的应用使用标准的UINavigationViewController。
在某些时候,用户已导航到一个显示SceneKit场景的ViewController。
这个场景在内存中非常沉重,因为它有大约6000个几何体,每个几何体都有自己的材质(这需要这样)。
当用户点击顶部栏中的后退按钮以返回上一个viewController时,应用程序会正确弹出当前的View Controller并显示下面的那个。
然而,大约4秒钟,新ViewController的UI显示为冻结。
我使用过仪器时间分析器,我可以看到这些SceneKit方法占用了4秒钟的大部分时间:
- [NSConcreteMapTable countByEnumeratingWithState:objects:count:]
- [SCNMetalResourceManager _geometryWillDie:]
- [SCNMetalResourceManager _materialWillDie:]
这是有道理的,因为有太多的几何形状和材料。
如何解决这种情况,以便在释放繁重的场景时不会阻止UI,无论是从SceneKit透视图(更快地进行释放)还是从UINavigationViewController透视图(可能强制将场景的重新分配发生在单独的线程?)。
答案 0 :(得分:0)
我的理解是SceneKit使用它自己的后台线程来完成工作,因此它不应该阻塞主线程。
主线程的阻塞可能与UINavigationController动画内容有关。我在尝试动画SCNViews / SCNScenes时遇到了问题,看起来SceneKit和CoreAnimation的协作效果不佳。
我这里没有完整的上下文,但我可以建议你测试几件事情:
您可以尝试将具有繁重场景scnView.scene = [SCNScene scene];
的SCNView的场景属性设置为在场景被弹出的视图控制器之前的新空场景,以便不涉及沉重的场景在动画中,SceneKit正确地将其解除分配给它的背景威胁。
您可以尝试对视图控制器中的繁重场景进行强有力的引用准备有问题的视图控制器与繁重的场景。
然后当弹出后者并返回到它下面的那个时,弹出的View Controller及其所有内容应该已经解除分配,除了繁重的场景,你将有一个对它的引用。
在这里你可以尝试设置为nil(动画完成后),并且可能会在SceneKit后台线程上正确解除分配。
如果没有,您可以先删除所有heavyScene.rootNode
子节点,操作等,然后取消分配。
如果这仍然不起作用,您可以先尝试使用递归函数逐个遍历所有节点,先取其几何/材质,然后取消分配场景本身。
使用渲染循环-renderer:updateAtTime:
方法删除多个帧(不到一秒)的节点。然后你可以确定SceneKit将以它的设计方式处理它们。
您必须检测用户何时按下UINavigationController“后退按钮”,并且弹出沉重场景的VC。然后结束,比如20帧(60fps的1/3秒),每帧删除5%的节点,所以仍然会有短暂的延迟但是1/3秒,并且希望不会引起注意。
可以通过覆盖其didMoveToParentViewController:
方法并检查nil
是否作为parent
参数传递来检测正在弹出的VC。
E.g。
- (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
}
}