NavigationStack中StateObject/View Model无法即时释放的问题求助
问题复现
以下代码中,使用@StateObject绑定的MyModel在通过NavigationLink跳转后,已消失视图的Model不会立即释放,仅在返回导航栈时才会被销毁:
import SwiftUI @MainActor class MyModel: ObservableObject { let letvalue: Int @Published var value: Int init(_ value: Int) { print("init model \(value)") self.value = value self.letvalue = value } deinit { print("deinit model \(letvalue)") } } struct MyView: View { @StateObject var model: MyModel init(_ value: Int) { self._model = StateObject(wrappedValue: MyModel(value)) } var body: some View { VStack { Text("Hello \(model.value)") NavigationLink(destination: { MyView(model.value + 1) }, label: { Text("goto \(model.value + 1)") }) } .onAppear { [weak model] in print("onAppear \(model?.value)") } .onDisappear { [weak model] in print("onDisappear \(model?.value)") } } } struct ContentView: View { var body: some View { NavigationStack { MyView(0) .padding() } } }
运行日志显示,跳转后仅触发onDisappear,但Model的deinit未执行:
init model 0 deinit model 0 init model 0 onAppear Optional(0) init model 1 onAppear Optional(1) onDisappear Optional(0) init model 2 onAppear Optional(2) onDisappear Optional(1) init model 3 onAppear Optional(3) onDisappear Optional(2)
问题原因
SwiftUI的NavigationStack默认缓存栈内已消失的视图实例,用于优化返回时的加载速度,但这会导致绑定的ObservableObject(包括@StateObject/@ObservedObject)无法及时释放,对于包含AVPlayer等资源密集型对象的Model,会造成持续的资源占用。
解决方案
方案1:使用navigationDestination配合编程式导航
通过navigationDestination结合NavigationLink的item机制,让SwiftUI仅在需要时创建视图实例,跳转后前一个视图的Model会立即被释放:
import SwiftUI @MainActor class MyModel: ObservableObject { let letvalue: Int @Published var value: Int init(_ value: Int) { print("init model \(value)") self.value = value self.letvalue = value } deinit { print("deinit model \(letvalue)") } } struct MyView: View { let value: Int @StateObject private var model: MyModel @State private var nextValue: Int? @Environment(\.dismiss) private var dismiss init(_ value: Int) { self.value = value self._model = StateObject(wrappedValue: MyModel(value)) } var body: some View { VStack { Text("Hello \(model.value)") Button("goto \(model.value + 1)") { nextValue = model.value + 1 } Button("返回") { dismiss() } } .navigationDestination(item: $nextValue) { val in MyView(val) } .onAppear { print("onAppear \(model.value)") } .onDisappear { print("onDisappear \(model.value)") } } } struct ContentView: View { var body: some View { NavigationStack { MyView(0) .padding() } } }
修改后的日志会显示Model在跳转后立即释放:
init model 0 onAppear 0 init model 1 onAppear 1 onDisappear 0 deinit model 0 init model 2 onAppear 2 onDisappear 1 deinit model 1
方案2:手动在onDisappear中清理资源
如果无法修改导航逻辑,可在视图消失时手动清理Model中的资源(如AVPlayer暂停、置空),即使Model未被释放,也能避免资源浪费:
import SwiftUI import AVFoundation @MainActor class MyModel: ObservableObject { let letvalue: Int @Published var value: Int var player: AVPlayer? // 示例资源密集型对象 init(_ value: Int) { print("init model \(value)") self.value = value self.letvalue = value // 初始化资源 if let url = URL(string: "https://example.com/video.mp4") { player = AVPlayer(url: url) } } // 资源清理方法 func cleanUpResources() { player?.pause() player = nil print("resources cleaned for model \(letvalue)") } deinit { print("deinit model \(letvalue)") } } struct MyView: View { @StateObject var model: MyModel init(_ value: Int) { self._model = StateObject(wrappedValue: MyModel(value)) } var body: some View { VStack { Text("Hello \(model.value)") NavigationLink(destination: { MyView(model.value + 1) }, label: { Text("goto \(model.value + 1)") }) } .onAppear { [weak model] in print("onAppear \(model?.value)") } .onDisappear { [weak model] in print("onDisappear \(model?.value)") model?.cleanUpResources() // 触发资源清理 } } }
方案3:自定义导航管理器控制Model生命周期
通过自定义导航栈管理器,手动管理每个页面的Model实例,当页面被移除时主动释放Model:
import SwiftUI @MainActor class NavigationManager: ObservableObject { @Published var path: [Int] = [] private var modelStore: [Int: MyModel] = [:] // 推入新页面并创建Model func push(to value: Int) { let model = MyModel(value) modelStore[value] = model path.append(value) } // 弹出页面并释放对应Model func pop() { guard let lastValue = path.last else { return } path.removeLast() modelStore[lastValue] = nil } } @MainActor class MyModel: ObservableObject { let letvalue: Int @Published var value: Int init(_ value: Int) { print("init model \(value)") self.value = value self.letvalue = value } deinit { print("deinit model \(letvalue)") } } struct MyView: View { let value: Int @ObservedObject var model: MyModel @EnvironmentObject var navManager: NavigationManager var body: some View { VStack { Text("Hello \(model.value)") Button("goto \(model.value + 1)") { navManager.push(to: model.value + 1) } Button("返回") { navManager.pop() } } .onAppear { print("onAppear \(model.value)") } .onDisappear { print("onDisappear \(model.value)") } } } struct ContentView: View { @StateObject private var navManager = NavigationManager() var body: some View { NavigationStack(path: $navManager.path) { MyView(value: 0, model: MyModel(0)) .padding() .navigationDestination(for: Int.self) { value in if let model = navManager.modelStore[value] { MyView(value: value, model: model) } } } .environmentObject(navManager) } }
内容的提问来源于stack exchange,提问作者twodayslate
相关产品推荐
相关产品推荐

