You need to enable JavaScript to run this app. SwiftUI中@ObservedObject实例无法释放的内存泄漏问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 22:36:03