加载USDZ文件至ARSCNView时出现内存崩溃问题求助
ARKit加载多个.usdz实例时因内存问题崩溃
我现在在把.usdz文件加载到ARSCNView中,单个加载运行正常,但加载多个对象实例时应用就会因为内存问题崩溃,崩溃提示是Terminated due to memory issue。我用的是苹果官方的.usdz示例文件,其中机器人文件大小约13.5MB,加载4-5个实例后就崩溃了。想问问ARKit应用的内存限制真的这么小吗,还是我的代码有问题?
我的代码如下:
// My touch point on the screen let touchLocation = sender.location(in: sceneView) // We have a touch point on an ARPlane if let result = self.sceneView.hitTest(touchLocation, types: ARHitTestResult.ResultType.existingPlaneUsingExtent).last { let position = SCNVector3Make(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z) // Load the .usdz model guard let usdzURL = Bundle.main.url(forResource: "toy_robot_vintage", withExtension: "usdz") else { return } // Create a node, set position and scale let referenceNode = SCNReferenceNode(url: usdzURL)! referenceNode.load() referenceNode.position = position referenceNode.scale = SCNVector3Make(0.01, 0.01, 0.01) // Add node to scene sceneView.scene.rootNode.addChildNode(referenceNode) }
问题分析与解决方案
首先明确:ARKit本身的内存限制并没有这么苛刻,核心问题出在你的代码加载模型的方式上。每次点击都重新创建SCNReferenceNode并加载整个.usdz资源,会导致重复加载相同的模型资源到内存中——虽然单个文件是13.5MB,但实际解析后在内存中的3D模型数据(顶点、纹理、材质等)远大于这个大小,多次重复加载就会快速耗尽内存。
下面是具体的优化方案:
1. 预加载并复用模型资源
你应该只加载一次.usdz模型,之后每次添加实例时,复用已加载的节点副本,而不是重新从文件加载。
修改代码的思路:
- 在类的属性中提前预加载好
SCNReferenceNode - 每次点击时,创建这个节点的
clone(),再设置位置和添加到场景
示例代码:
// 类级别属性,预加载模型 private var robotReferenceNode: SCNReferenceNode? // 在viewDidLoad或者合适的地方预加载 override func viewDidLoad() { super.viewDidLoad() guard let usdzURL = Bundle.main.url(forResource: "toy_robot_vintage", withExtension: "usdz") else { return } robotReferenceNode = SCNReferenceNode(url: usdzURL)! robotReferenceNode?.load() // 提前设置好缩放,避免每次clone后重复设置 robotReferenceNode?.scale = SCNVector3Make(0.01, 0.01, 0.01) } // 点击事件处理 @IBAction func handleTap(_ sender: UITapGestureRecognizer) { let touchLocation = sender.location(in: sceneView) guard let result = sceneView.hitTest(touchLocation, types: .existingPlaneUsingExtent).last, let robotTemplate = robotReferenceNode else { return } let position = SCNVector3(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z) // 克隆已加载的节点 let robotNode = robotTemplate.clone() robotNode.position = position sceneView.scene.rootNode.addChildNode(robotNode) }
这样处理后,不管添加多少个实例,内存中只会保留一份模型的原始资源,所有实例共享这份资源,内存占用会大幅降低。
2. 额外的内存优化建议
- 及时清理不再需要的节点:如果场景中的模型实例不再需要显示,记得调用
removeFromParentNode(),并确保没有其他强引用持有这些节点,让ARC能及时释放内存。 - 检查纹理和材质:有些.usdz文件可能包含高分辨率纹理,你可以尝试降低纹理分辨率(比如用工具压缩纹理)来减少内存占用。
- 使用Xcode内存调试工具:通过Xcode的Memory Graph Debugger查看内存占用情况,确认是否有内存泄漏或者不必要的资源重复加载。比如查看是否有多个相同的
SCNGeometry或SCNSMaterial实例存在。 - 考虑使用
SCNSceneSource:如果需要更精细的资源控制,可以用SCNSceneSource加载.usdz文件,提取其中的节点模板,再进行克隆复用,效果和SCNReferenceNode类似,但更灵活。
为什么原来的代码会崩溃?
SCNReferenceNode(url:)配合load()会每次从磁盘读取文件并解析成完整的3D资源,包括顶点数据、纹理、材质等。这些数据都会被加载到内存中,而且每个新的SCNReferenceNode都会创建一份独立的资源副本——4-5次这样的操作,内存占用会快速累积到系统无法承受的程度,最终触发内存崩溃。
内容的提问来源于stack exchange,提问作者Pikebu
相关产品推荐
相关产品推荐

