如何释放 RealityKit ARView()?

6
我无法将我的RealityKit ARView()从内存中释放。
我知道使用ARKit+SceneKit时有(或可能有)类似的问题 - 例如这个解决方案:https://dev59.com/4lQJ5IYBdhLWcg3w8anH#53919730,但不幸的是它并不能解决我的问题。
以上解决方案通常通过手动删除所有“可疑”的内容来实现。这正是我在更广泛的范围内所做的。
class ViewController_ARViewMemoryTest: UIViewController {

    var arView: ARView?

    init() {
        super.init(nibName: nil, bundle: nil)
        arView = ARView(frame: .zero)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit {
        doEverythingThatsNeeded()
    }

    public func doEverythingThatsNeeded() {
        self.arView?.session.pause()
        self.arView?.session.delegate = nil
        self.arView?.removeFromSuperview()
        // Quite a few more removals and resets here, ie. for Combines AnyCancellables
        self.arView = nil
    }

}

我也会在外部调用doEverythingThatsNeeded()函数:
aRViewMemoryTest?.doEverythingThatsNeeded()
aRViewMemoryTest?.arView = nil
aRViewMemoryTest = nil

问题似乎与我将ARView或UIViewController包装在SwiftUI的UIViewRepresentable / UIViewControllerRepresentable中无关。
我相信这必须是一个错误,并且几个月前我已经提交了报告。但是我希望有一些解决方法,在Apple修复潜在问题之前能够帮助解决问题。
非常感谢!

1
你有找到什么东西吗? - Chirag Shah
1
两年后,问题似乎仍然存在。 - HelloTimo
1个回答

2

RealityKit v2.0 | 2023年3月30日

UIKit版本

以下是您可以在UIKit中开始解除分配过程的方法:

import UIKit
import RealityKit

class ViewController: UIViewController {
    
    var arView: ARView? = ARView(frame: .zero)

    deinit { removingView() }
    
    func addingView() {
        self.arView?.frame = .init(x: 0, y: 0, width: 300, height: 700)
        self.view.addSubview(arView!)
    }
    
    func removingView() {
        self.arView?.session.pause()            // there's no session on macOS
        self.arView?.session.delegate = nil     // there's no session on macOS
        self.arView?.scene.anchors.removeAll()
        self.arView?.removeFromSuperview()
        self.arView?.window?.resignKey()
        self.arView = nil
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()            
        addingView()
        
        let boxAnchor = try! Experience.loadBox()
        arView?.scene.anchors.append(boxAnchor)
        
        removingView()
    }
}

然而,真正的问题在于当ARView被解构时,为其分配的内存并没有完全释放(尽管代码是100%正确的)。不过,也有好消息 - 当您再次使用此ARView时,将使用相同的已分配内存。
如果需要更多信息,请阅读this post以获取详细信息。


SwiftUI版本

由于SwiftUI视图是值类型,因此不需要像UIKit中那样进行销毁处理(我指的是deinit方法)。但是,如果我们从superview中删除ARView,则仍会发生内存泄漏。

enter image description here

import SwiftUI
import RealityKit

struct ContentView : View {
        
    var body: some View {
        NavigationStack {
            ZStack {
                Color.black
                                
                VStack {
                    NavigationLink(destination: RealityKitView()) {
                        Text("Load AR Scene")
                            .foregroundColor(.mint)
                            .font(.title2)
                    }
                }
            }.ignoresSafeArea()
        }
    }
}

这里是ARViewContainer的init(isTapped: Binding<Bool>)
struct RealityKitView : View {
    
    @State private var isTapped: Bool = false
    
    var body: some View {
        ZStack {
            Color.orange.ignoresSafeArea()
            Text("ARView was removed")
                .font(.title3)
            
            ARViewContainer(isTapped: $isTapped)
                .ignoresSafeArea()
            VStack {
                Spacer()
                Button("Remove ARView") {
                    isTapped = true
                }
                .disabled(isTapped ? true : false)
            }
        }
    }
}

然后,让我们实现一个方法,将视图与其父视图解除链接。

struct ARViewContainer : UIViewRepresentable {
    
    let arView = ARView(frame: .zero)
    @Binding var isTapped: Bool
    
    func makeUIView(context: Context) -> ARView {
        let model = ModelEntity(mesh: .generateSphere(radius: 0.25))
        let anchor = AnchorEntity()
        model.setParent(anchor)
        arView.scene.anchors.append(anchor)
        return arView
    }
    func updateUIView(_ view: ARView, context: Context) {
        if isTapped {
            view.removeFromSuperview()
        }
    }
}

附言:

唉,苹果工程师还没有解决 ARView 的内存泄漏问题...


谢谢!我没有使用Experience。但是除此之外,我已经将我的代码剥离到只有AppDelegate和你的类,再加上一个按钮,它将视图控制器设置为nil:仍然没有成功。 - HelloTimo
我不确定我是在处理框架错误还是只是做错了什么。欢迎任何意见。 - HelloTimo
1
@AndyJazz,SwiftUI版本可以工作,但应该注意updateUIView可能会在需要卸载ARViewContainer之前被调用。感谢您的答案,它确实有助于释放容器。 - Jan-Michael Tressler
updateUIView 中执行 uiView.removeFromSuperview() 的意义是什么?这样做根本没有作用,因为它会在从 makeUIView 创建 ARView 后将其删除。 - XcodeNOOB
我刚刚演示了在SwiftUI中删除视图有多么容易(添加了asyncAfter)。 - Andy Jazz
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接