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

销毁MainView后如何移除environmentObject并避免崩溃?

问题描述

我有如下MainView视图结构:

struct MainView: View{
    @EnvironmentObject var appModel: AppDataModel
    @State var localAppDataModel: AppDataModel! = AppDataModel()
    var body: some View{
        VStack {
            ContentView()
        }
        Button(action: {
            appModel.dismissAR()
        },
               label: {
            Text(LocalizedString.close)
                .modifier(VisualEffectRoundedCorner())
        })
        .environmentObject(localAppDataModel)
        .onDisappear{
            localAppDataModel.objectCaptureSession?.cancel()
            localAppDataModel.photogrammetrySession?.cancel()
            localAppDataModel.objectCaptureSession = nil
            localAppDataModel.photogrammetrySession = nil
            localAppDataModel.scanFolderManager = nil
            localAppDataModel.showPreviewModel = false
            localAppDataModel.orbit = .orbit1
            localAppDataModel.orbitState = .initial
            localAppDataModel.isObjectFlipped = false
            localAppDataModel = nil
        }
    }

通过UIKit的如下方法展示该视图:

public static func present(viewC: UIViewController){
    let controller = UIHostingController(rootView: MainView(appModel: .init()))
    mainController = controller
    mainController?.modalPresentationStyle = .fullScreen
    guard let mainController else { return }
    viewC.present(mainController, animated: true, completion: nil)
}
var mainController: UIHostingController<MainView>?

调用以下方法销毁MainView时:

public class AppDataModel: ObservableObject, Identifiable {
    // ...
    func dismissAR(){
        mainController?.dismiss(animated: true)
    }
    // ...
}

应用会崩溃,原因是localAppDataModel变为nil。请问如何在销毁MainView的同时,正确释放/移除localAppDataModel这个environmentObject?

解决方案

1. 移除onDisappear中直接置空localAppDataModel的代码

崩溃核心原因是:调用dismissAR()后视图进入销毁流程,onDisappear触发时,虽然你先清理了模型属性,但最后将localAppDataModel置为nil,此时可能仍有子视图(比如ContentView)在引用该模型,导致野指针访问崩溃。

修改后的onDisappear代码:

.onDisappear{
    localAppDataModel.objectCaptureSession?.cancel()
    localAppDataModel.photogrammetrySession?.cancel()
    localAppDataModel.objectCaptureSession = nil
    localAppDataModel.photogrammetrySession = nil
    localAppDataModel.scanFolderManager = nil
    localAppDataModel.showPreviewModel = false
    localAppDataModel.orbit = .orbit1
    localAppDataModel.orbitState = .initial
    localAppDataModel.isObjectFlipped = false
    // 删除此行:localAppDataModel = nil
}

删除置空代码后,让ARC自动管理模型的内存释放即可。

2. 调整environmentObject的注入范围

当前仅给Button注入了localAppDataModel,如果ContentView也依赖该模型,需将注入范围提升至整个VStack,确保所有子视图共享同一模型实例,且生命周期与MainView绑定:

var body: some View{
    VStack {
        ContentView()
        Button(action: {
            appModel.dismissAR()
        },
               label: {
            Text(LocalizedString.close)
                .modifier(VisualEffectRoundedCorner())
        })
    }
    .environmentObject(localAppDataModel) // 将注入移至VStack外部
    .onDisappear{
        // 清理代码...
    }
}

3. 优化dismissAR的内存管理

将mainController改为弱引用避免循环引用,并在dismiss完成后清空引用:

// 改为弱引用
weak var mainController: UIHostingController<MainView>?

public static func present(viewC: UIViewController){
    let controller = UIHostingController(rootView: MainView(appModel: .init()))
    mainController = controller
    controller.modalPresentationStyle = .fullScreen
    viewC.present(controller, animated: true, completion: nil)
}

// 在AppDataModel的dismissAR方法中补充完成回调
func dismissAR(){
    mainController?.dismiss(animated: true) {
        mainController = nil // dismiss完成后清空引用
    }
}

4. 可选:用@StateObject替代@State管理本地模型

对于ObservableObject类型的本地状态,推荐使用@StateObject,它能更精准地管理ObservableObject的生命周期,避免不必要的重建或内存问题:

// 替换原@State定义
@StateObject var localAppDataModel = AppDataModel()

内容的提问来源于stack exchange,提问作者TheCesco1988

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:57:42