You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI嵌套@escaping闭包致@StateObject内存泄漏问题求助

SwiftUI嵌套@escaping闭包内存泄漏解决方案

问题核心

多层嵌套的@escaping闭包中,即便外层用了[weak viewModel],内层闭包如果隐式捕获外层的viewModel引用,加上IdentifiableAction被视图的@State持有,会形成循环引用链——视图持有@StateObject viewModel,viewModel被闭包捕获,闭包被IdentifiableAction包裹后存在@State中,最终导致内存泄漏。

关键解决思路

所有嵌套层级的闭包都必须显式添加弱引用捕获列表,并且用可选绑定确保引用有效后再执行逻辑,避免隐式强引用在嵌套链中传递。

修改后的代码示例

假设你的原实现代码如下:

struct IdentifiableAction: Identifiable, Hashable {
    let id = UUID()
    let action: () -> Void
    
    static func == (lhs: IdentifiableAction, rhs: IdentifiableAction) -> Bool {
        lhs.id == rhs.id
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

struct CheckModifier: ViewModifier {
    @Binding var pendingAction: IdentifiableAction?
    
    func body(content: Content) -> some View {
        content
            .onChange(of: pendingAction) { action in
                action?.action()
            }
    }
}

extension View {
    func check(action: Binding<IdentifiableAction?>) -> some View {
        self.modifier(CheckModifier(pendingAction: action))
    }
}

class DetailViewModel: ObservableObject {
    func finalAction() {
        print("执行最终操作")
    }
}

struct DetailView: View {
    @StateObject private var viewModel = DetailViewModel()
    @State private var pendingAction: IdentifiableAction?
    
    var body: some View {
        Button("触发多层检查") {
            pendingAction = IdentifiableAction { [weak viewModel] in
                print("执行检查1")
                // 原内层闭包未显式捕获weak viewModel,导致泄漏
                self.pendingAction = IdentifiableAction {
                    print("执行检查2")
                    self.pendingAction = IdentifiableAction {
                        print("执行检查3")
                        viewModel?.finalAction()
                    }
                }
            }
        }
        .check(action: $pendingAction)
    }
}

修改闭包捕获逻辑后的关键代码:

Button("触发多层检查") {
    pendingAction = IdentifiableAction { [weak viewModel] in
        print("执行检查1")
        guard let viewModel = viewModel else { return }
        // 内层闭包显式添加weak捕获
        self.pendingAction = IdentifiableAction { [weak viewModel] in
            print("执行检查2")
            guard let viewModel = viewModel else { return }
            // 最内层同样显式捕获
            self.pendingAction = IdentifiableAction { [weak viewModel] in
                print("执行检查3")
                viewModel.finalAction()
            }
        }
    }
}

额外优化建议

  • 如果需要访问视图的pendingAction,建议同时捕获weak self,避免意外持有视图引用:
pendingAction = IdentifiableAction { [weak self, weak viewModel] in
    guard let self = self, let viewModel = viewModel else { return }
    print("执行检查1")
    self.pendingAction = IdentifiableAction { [weak self, weak viewModel] in
        guard let self = self, let viewModel = viewModel else { return }
        print("执行检查2")
        self.pendingAction = IdentifiableAction { [weak viewModel] in
            guard let viewModel = viewModel else { return }
            print("执行检查3")
            viewModel.finalAction()
        }
    }
}
  • 若能确保viewModel生命周期长于闭包执行周期,可尝试用[unowned viewModel]替代weak,但需注意避免野指针访问风险。

内容的提问来源于stack exchange,提问作者Michał Ziobro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:59:02