iOS 15中使用@Published变量时如何销毁StateObject?
iOS 15 中 StateObject 绑定 .alert() 导致内存泄漏的解决方案
在 iOS 15 环境下,当使用 StateObject 的 @Published 属性绑定 .alert() 的 isPresented 参数时,会出现 ViewModel 无法触发 deinit 的内存泄漏问题——这是因为 View 持有了 @Published 属性的 Publisher 引用,导致整个 StateObject 无法被销毁。iOS 16 已修复此问题,以下是针对 iOS 15 的几种解决办法:
方法一:使用本地 @State 中转绑定
通过本地 @State 变量作为 .alert() 的绑定源,再同步 StateObject 中的属性,避免直接绑定 @Published 属性导致的引用持有:
struct ChildView: View { @StateObject private var vm = ChildViewModel() @State private var localShowingAlert = false // 本地中转变量 var body: some View { Text("Hello, World!") .alert("Alert Title", isPresented: $localShowingAlert, actions: { Button("OK") { } }, message: { Text("Alert Message") }) // 同步 ViewModel 的属性到本地变量 .onReceive(vm.$showingAlert) { newValue in localShowingAlert = newValue } // 同步本地变量的变化回 ViewModel .onChange(of: localShowingAlert) { newValue in if !newValue { vm.showingAlert = newValue } } } }
方法二:自定义 Binding 避免持有 Publisher
创建自定义 Binding,直接访问 StateObject 的属性而不使用自动生成的 $ 绑定(该绑定会持有 Publisher):
struct ChildView: View { @StateObject private var vm = ChildViewModel() var body: some View { Text("Hello, World!") .alert("Alert Title", isPresented: Binding( get: { vm.showingAlert }, set: { vm.showingAlert = $0 } ), actions: { Button("OK") { } }, message: { Text("Alert Message") }) } }
方法三:在 View 消失时重置属性并断开关联
利用 .onDisappear 回调,在 View 销毁前重置 @Published 属性,帮助打破引用循环:
struct ChildView: View { @StateObject private var vm = ChildViewModel() var body: some View { Text("Hello, World!") .alert("Alert Title", isPresented: $vm.showingAlert, actions: { Button("OK") { } }, message: { Text("Alert Message") }) .onDisappear { vm.showingAlert = false } } }
以上三种方法中,方法一和方法二更可靠,能彻底避免引用持有问题;方法三作为临时 workaround,适用于简单场景。
内容的提问来源于stack exchange,提问作者Stan
相关产品推荐
相关产品推荐

