You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

加载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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:24:47