SwiftUI中@ObservedObject实例无法释放的内存泄漏问题咨询
问题根因
很多人会误以为值类型不会引发引用循环,这个场景的核心误区就在这里:
- 虽然
ViewB是值类型的结构体,但闭包对值类型的实例方法进行捕获时,会将整个ViewB实例拷贝并持有 ViewB中包含@ObservedObject修饰的model属性,这个属性包装器内部会对传入的Model实例产生强引用- 此时就形成了完整的引用循环:
ContentView通过@StateObject强持有Model→Model强持有action闭包 →action闭包强持有捕获到的ViewB实例 →ViewB实例强持有Model,最终导致Model实例永远无法被释放。
解决方案
下面给出3种常用的可行方案,按需选择即可:
- 方案1:避免捕获ViewB实例,直接在闭包内实现逻辑
如果你的bAction方法不需要访问ViewB的其他属性,可以直接把逻辑写在闭包内,不调用实例方法就不会触发对ViewB的捕获,自然不会形成循环:
.onAppear { model.action = { print("Hey!") } }
- 方案2:主动在生命周期结束时清理闭包
在ViewB消失时主动将model的action置空,手动断开引用链:
struct ViewB: View { @ObservedObject var model: Model var body: some View { Color.red.frame(width: 20, height: 20) .onAppear { model.action = { bAction() } } .onDisappear { model.action = nil } } private func bAction() { print("Hey!") } }
- 方案3:单独捕获需要的属性,避免捕获整个ViewB实例
Swift中值类型不支持weak/unowned修饰符,如果bAction需要用到ViewB的属性,可以将需要用到的值单独放入捕获列表,避免捕获整个ViewB实例,也就不会携带对Model的强引用:
// 示例假设bAction需要用到ViewB的id属性 struct ViewB: View { @ObservedObject var model: Model let id = 1001 var body: some View { Color.red.frame(width: 20, height: 20) .onAppear { // 仅捕获id,不捕获整个ViewB model.action = { [id] in print("Hey! \(id)") } } } }
内容的提问来源于stack exchange,提问作者msmialko
相关产品推荐
相关产品推荐

