RealityKit调用deinit后内存未完全释放,ARView实例无法彻底释放内存的问题求助
我之前也踩过RealityKit内存释放的坑,结合实践经验和对框架的理解,来帮你拆解可能的原因和解决思路:
可能的原因分析
1. ARSession的隐式强引用残留
你已经做了暂停session、置空delegate的操作,但RealityKit的ARView和ARSession之间存在一些框架内部的隐式强引用——比如session可能还持有摄像头帧缓存、空间映射数据的引用,这些不会仅仅通过pause()就立刻释放。常规的清理操作可能没触及到这些深层关联。
2. 场景资源的递归引用未清理
你移除了所有scene anchors,但每个anchor下的实体(比如ModelEntity)可能还持有材质、模型文件等资源的强引用,甚至这些资源可能被RealityKit的全局资源缓存池持有,手动移除anchors并不会自动递归清理这些子实体的资源。
3. 系统级AR资源缓存
iOS系统本身会对AR相关的资源做复用缓存,比如ARKit的跟踪数据、摄像头纹理缓存等。这些缓存不会在你关闭ARView后立刻被回收,而是留在内存中供后续AR会话快速启动,这部分内存占用是系统层面的,不属于内存泄漏。
4. 循环引用未被发现
如果你的ViewController或其他对象通过闭包、自定义delegate持有了ARView的强引用(比如闭包没加weak self),会导致ARView无法被正常deinit,内存自然无法释放。
针对性的改进方案
1. 更彻底的ARSession清理
在暂停session后,尝试用空配置重置session,强制清理内部状态:
self.arView?.session.pause() // 用空配置重置跟踪,移除所有现有锚点 let emptyConfig = ARConfiguration() self.arView?.session.run(emptyConfig, options: [.resetTracking, .removeExistingAnchors]) self.arView?.session.pause() self.arView?.session.delegate = nil
2. 递归清理所有实体资源
遍历所有锚点和子实体,手动清理模型、材质等资源,断开所有引用链:
func clearEntities(_ entity: Entity) { for child in entity.children { clearEntities(child) child.removeFromParent() // 清理ModelEntity的模型和材质 if let modelEntity = child as? ModelEntity { modelEntity.model = nil modelEntity.materials.removeAll() } // 移除AnchorEntity的场景关联 if let anchorEntity = child as? AnchorEntity { anchorEntity.removeFromScene() } } } // 遍历所有锚点并清理 self.arView?.scene.anchors.forEach { anchor in clearEntities(anchor) anchor.removeFromScene() } self.arView?.scene.anchors.removeAll()
3. 断开视图的所有外部引用
除了removeFromSuperview(),还要置空window引用,确保没有其他视图层级持有ARView:
self.arView?.removeFromSuperview() self.arView?.window = nil self.arView = nil
4. 排查循环引用
用Xcode的Memory Graph Debugger(调试栏中的内存图标)查看内存中的对象,找到哪些对象还持有ARView的引用——重点检查闭包、自定义代理是否使用了weak或unowned修饰。
5. 区分系统缓存和内存泄漏
如果清理后内存维持在175MB不再上涨,尝试再次创建并关闭ARView:
- 如果第二次关闭后内存没有继续增加,说明第一次的残留是系统缓存,属于正常现象,iOS会在内存紧张时自动回收。
- 如果内存持续上涨,那大概率是存在未被清理的强引用,需要进一步排查。
完整改进后的清理函数
func closeCurrentView(completion: (() -> Void)? = nil, isScanAnotherFloor: Bool = false) { // 结束后台任务 if backgroundRenderingID != UIBackgroundTaskIdentifier.invalid { UIApplication.shared.endBackgroundTask(self.backgroundRenderingID) self.backgroundRenderingID = UIBackgroundTaskIdentifier.invalid } // 彻底清理ARSession self.arView?.session.pause() let emptyConfig = ARConfiguration() self.arView?.session.run(emptyConfig, options: [.resetTracking, .removeExistingAnchors]) self.arView?.session.pause() self.arView?.session.delegate = nil // 递归清理实体资源 func clearEntities(_ entity: Entity) { for child in entity.children { clearEntities(child) child.removeFromParent() if let modelEntity = child as? ModelEntity { modelEntity.model = nil modelEntity.materials.removeAll() } if let anchorEntity = child as? AnchorEntity { anchorEntity.removeFromScene() } } } self.arView?.scene.anchors.forEach { anchor in clearEntities(anchor) anchor.removeFromScene() } self.arView?.scene.anchors.removeAll() // 移除视图并断开引用 self.arView?.removeFromSuperview() self.arView?.window = nil self.arView = nil // 触发自动释放池清理(调试用,Release环境可省略) autoreleasepool {} completion?() }
内容的提问来源于stack exchange,提问作者Chirag Shah

