使用ARKit+RealityKit+Vision的AR视图内存泄漏问题求助
AR视图内存泄漏导致应用崩溃问题
我搭建了一个示例项目,每次关闭HandInteractionARViewContainer124视图时内存都无法释放,进入该视图4-5次后应用就会崩溃。我听说NavigationView存在内存问题,因此改用了最新的NavigationStack实现导航:
struct Nav : View { var body: some View { NavigationStack { VStack { NavigationLink(destination: { HandInteractionARViewContainer124().edgesIgnoringSafeArea(.all) }, label: { Text("AR View") .padding(50) }) NavigationLink(destination: { HandInteractionARViewContainer124().edgesIgnoringSafeArea(.all) }, label: { Text("AR View 2") .padding(50) }) } } } }
核心实现部分:加载RC文件,并利用Vision实现手部触碰Box实体时发送通知触发器的功能:
struct HandInteractionARViewContainer124: UIViewControllerRepresentable { func makeUIViewController(context: UIViewControllerRepresentableContext<HandInteractionARViewContainer124>) -> HandInteractionARViewController124 { let viewController = HandInteractionARViewController124() return viewController } func updateUIViewController(_ uiViewController: HandInteractionARViewController124, context: UIViewControllerRepresentableContext<HandInteractionARViewContainer124>) { } func makeCoordinator() -> HandInteractionARViewContainer124.Coordinator { return Coordinator() } class Coordinator { } } class HandInteractionARViewController124: UIViewController, ARSessionDelegate { private var arView:ARView! let sceneUno = try! K128.loadScene() lazy var request:VNRequest = { var handPoseRequest = VNDetectHumanHandPoseRequest(completionHandler: handDetectionCompletionHandler) handPoseRequest.maximumHandCount = 1 return handPoseRequest }() var viewWidth:Int = 0 var viewHeight:Int = 0 var box:ModelEntity! var boxButton:ModelEntity! override func viewDidLoad() { super.viewDidLoad() arView = ARView(frame: view.bounds) arView.session.delegate = self view.addSubview(arView) let config = ARWorldTrackingConfiguration() config.environmentTexturing = .automatic config.frameSemantics = [.personSegmentation] config.planeDetection = [.horizontal] arView.session.run(config, options: []) viewWidth = Int(arView.bounds.width) viewHeight = Int(arView.bounds.height) setupObject() } private func setupObject(){ let anchor = AnchorEntity(plane: .horizontal) let plane = ModelEntity(mesh: .generatePlane(width: 2, depth: 2), materials: [OcclusionMaterial()]) anchor.addChild(plane) plane.generateCollisionShapes(recursive: false) plane.physicsBody = PhysicsBodyComponent(massProperties: .default, material: .default, mode: .static) box = ModelEntity(mesh: .generateBox(size: 0.05), materials: [SimpleMaterial(color: .white, isMetallic: true)]) box.generateCollisionShapes(recursive: false) box.physicsBody = PhysicsBodyComponent(massProperties: .default, material: .default, mode: .dynamic) box.position = [0,0.025,0] anchor.addChild(sceneUno) anchor.addChild(box) arView.scene.addAnchor(anchor) } var recentIndexFingerPoint:CGPoint = .zero func handDetectionCompletionHandler(request: VNRequest?, error: Error?) { guard let observation = request?.results?.first as? VNHumanHandPoseObservation else { return } guard let indexFingerTip = try? observation.recognizedPoints(.all)[.indexTip], indexFingerTip.confidence > 0.3 else {return} let normalizedIndexPoint = VNImagePointForNormalizedPoint(CGPoint(x: indexFingerTip.location.y, y: indexFingerTip.location.x), viewWidth, viewHeight) if let entity = arView.entity(at: normalizedIndexPoint) as? ModelEntity, entity == box { entity.addForce([0,40,0], relativeTo: nil) sceneUno.notifications.flipFlop.post() } recentIndexFingerPoint = normalizedIndexPoint } func session(_ session: ARSession, didUpdate frame: ARFrame) { let pixelBuffer = frame.capturedImage DispatchQueue.global(qos: .userInitiated).async { [weak self] in let handler = VNImageRequestHandler(cvPixelBuffer:pixelBuffer, orientation: .up, options: [:]) do { try handler.perform([(self?.request)!]) } catch let error { print(error) } } } }
内存泄漏修复方案
1. 清理ARSession代理与资源
视图销毁时必须移除ARSession代理并停止会话,避免ARSession持有控制器引用:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 移除代理 arView.session.delegate = nil // 暂停会话 arView.session.pause() // 清理AR场景锚点 arView.scene.anchors.removeAll() // 移除ARView并置空 arView.removeFromSuperview() arView = nil // 置空实体引用 box = nil }
2. 修复Vision请求的强引用问题
将request改为可选类型,在视图销毁时取消请求,同时安全访问self避免强制解包:
// 修改request为可选 var request: VNRequest? override func viewDidLoad() { super.viewDidLoad() // ...其他初始化代码 // 初始化Vision请求 request = VNDetectHumanHandPoseRequest(completionHandler: handDetectionCompletionHandler) request?.maximumHandCount = 1 } // 修改session方法中的请求执行逻辑 func session(_ session: ARSession, didUpdate frame: ARFrame) { let pixelBuffer = frame.capturedImage DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self, let request = self.request else { return } let handler = VNImageRequestHandler(cvPixelBuffer:pixelBuffer, orientation: .up, options: [:]) do { try handler.perform([request]) } catch let error { print(error) } } } // 在viewWillDisappear中取消请求 override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) request?.cancel() request = nil // ...其他清理代码 }
3. 移除强制解包风险
将try!改为错误处理,避免加载场景失败直接崩溃,同时确保资源能正确释放:
// 修改sceneUno为可选 var sceneUno: K128? override func viewDidLoad() { super.viewDidLoad() do { sceneUno = try K128.loadScene() setupObject() } catch { print("场景加载失败:\(error)") // 可添加错误提示逻辑 } // ...其他初始化代码 } // 修改setupObject方法,先检查sceneUno是否存在 private func setupObject(){ guard let sceneUno = sceneUno else { return } let anchor = AnchorEntity(plane: .horizontal) let plane = ModelEntity(mesh: .generatePlane(width: 2, depth: 2), materials: [OcclusionMaterial()]) anchor.addChild(plane) plane.generateCollisionShapes(recursive: false) plane.physicsBody = PhysicsBodyComponent(massProperties: .default, material: .default, mode: .static) box = ModelEntity(mesh: .generateBox(size: 0.05), materials: [SimpleMaterial(color: .white, isMetallic: true)]) box.generateCollisionShapes(recursive: false) box.physicsBody = PhysicsBodyComponent(massProperties: .default, material: .default, mode: .dynamic) box.position = [0,0.025,0] anchor.addChild(sceneUno) anchor.addChild(box) arView.scene.addAnchor(anchor) }
4. 优化SwiftUI视图的生命周期绑定
在HandInteractionARViewContainer124中添加onDisappear回调,确保控制器能及时收到清理通知:
// 修改NavigationLink的destination NavigationLink(destination: { HandInteractionARViewContainer124() .edgesIgnoringSafeArea(.all) .onDisappear { // 可在这里触发额外的清理逻辑,不过核心清理还是在控制器内部处理 } }, label: { Text("AR View") .padding(50) })
内容的提问来源于stack exchange,提问作者Theuns Botha
相关产品推荐
相关产品推荐

