SwiftUI中Coordinator跨视图跳转后无法deinit的问题排查
SwiftUI Coordinator多层导航返回后无法释放的解决方案
问题描述
当导航路径为 root -> FeatureC -> 返回 时,FeatureC对应的Coordinator能正常执行deinit;但路径为 root -> FeatureC -> FeatureD -> 返回 -> 返回 时,该Coordinator无法被销毁,期望回到root视图时能自动释放对象。
问题原因
在版本2的代码中,FeatureCCoordinator通过var dCoordinator: FeatureDCoordinator?持有了子Coordinator的强引用,而从FeatureD返回FeatureC后,这个强引用没有被主动清空。尽管FeatureDCoordinator使用unowned持有父Coordinator(不会增加引用计数),但父Coordinator对子的残留强引用会导致其在返回Root时无法正常释放——导航栈销毁FeatureCView后,父Coordinator的引用计数因持有子Coordinator而无法归零。
解决方案
通过监听FeatureC中导航到FeatureD的状态,在从FeatureD返回时主动清空父Coordinator对子Coordinator的引用,打破残留的强引用链。
修改后的代码示例
struct FeatureCView: View { @State private var isFeatureDPresented = false let coordinator: FeatureCCoordinator init(coordinator: FeatureCCoordinator = FeatureCCoordinator()) { self.coordinator = coordinator } var body: some View { VStack { Text("Feature C") Button { isFeatureDPresented = true } label: { Text("Feature D") } .buttonStyle(.borderedProminent) } .navigationDestination(isPresented: $isFeatureDPresented) { NavigationLazyView { featureDView } } // 监听导航状态,返回时清空子Coordinator引用 .onChange(of: isFeatureDPresented) { oldValue, newValue in if !newValue { coordinator.dCoordinator = nil } } } private var featureDView: some View { let dCoordinator = FeatureDCoordinator(cCoordinator: coordinator) coordinator.dCoordinator = dCoordinator return FeatureDView(coordinator: dCoordinator) } }
关键说明
- 引用关系规范:父子Coordinator的设计中,父可以持有子的强引用,子必须用
weak或unowned持有父(确保父生命周期长于子时用unowned),避免循环强引用。 - 主动清理引用:在子页面销毁时(通过导航状态变化监听),父Coordinator要及时清空对子的强引用,确保自身在生命周期结束时没有残留引用,能被系统正常回收。
内容的提问来源于stack exchange,提问作者Jason Rich Darmawan
相关产品推荐
相关产品推荐

