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

Swift传递方法引用时弱引用未按预期工作的问题排查

Swift强弱引用导致的内存泄漏问题解析

问题描述

我已经了解Swift中的强/弱引用概念,但运行第一段代码并点击按钮dismiss页面后,TestViewModel仍驻留在内存中。原本以为使用[weak viewmodel]足以避免内存泄漏,第二个例子通过修改传入的回调解决了问题,但不清楚原因。

环境:Swift 5、Xcode 14.2


代码示例1(存在内存泄漏)

TestScreen 代码

import SwiftUI
import Resolver

struct TestScreen: View {
    
    @StateObject var viewmodel = TestViewModel()
    @Injected var testStruct: TestStruct
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        NavigationView {
            
            VStack(spacing: 0) {
                
                Button("go back") { [weak viewmodel] in
                        testStruct.saveActionGlobaly(onAsyncAction:  viewmodel?.someAsyncAction )
                        presentationMode.wrappedValue.dismiss()
                    }
            }
        }
    }
}

TestStruct 代码

import Foundation
import Resolver
import SwiftUI

public class TestStruct {
   var onAsyncAction: (() async throws -> Void)?
    
    public func saveActionGlobaly(onAsyncAction: (() async throws -> Void)?) {
        self.onAsyncAction = onAsyncAction
    } 
}

代码示例2(修复内存泄漏)

TestScreen 代码

import Resolver

struct TestScreen: View {
    
    @StateObject var viewmodel = TestViewModel()
    @Injected var testStruct: TestStruct
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        NavigationView {
            
            VStack(spacing: 0) {
                
                Button("go back") { [weak viewmodel] in
                        testStruct.saveActionGlobaly(onAsyncAction:  { await viewmodel?.someAsyncAction() } )
                        presentationMode.wrappedValue.dismiss()
                    }
            }
        }
    }
}

原因分析

代码示例1泄漏的核心原因

直接传递viewmodel?.someAsyncAction时,这个方法引用隐式强持有了TestViewModel实例。Swift中,类实例的方法引用本质上是「实例+方法」的绑定组合,哪怕你在闭包捕获列表里写了[weak viewmodel],这里的方法引用并没有被闭包包裹,捕获列表的规则对它不生效。最终TestStruct的onAsyncAction属性会强持有TestViewModel,导致页面dismiss后ViewModel无法被释放。

代码示例2修复的原理

用匿名闭包{ await viewmodel?.someAsyncAction() }包裹方法调用时,闭包遵循捕获列表的规则,通过[weak viewmodel]弱引用ViewModel。此时TestStruct持有的是这个闭包,而闭包对ViewModel仅持有弱引用——当页面dismiss后,ViewModel的所有强引用被释放,引用计数归零,就能正常被系统回收,避免了内存泄漏。


关键点总结

  • 直接传递类实例的方法引用,会自动强绑定实例,捕获列表的weak设置无法影响这种绑定。
  • 必须用闭包包裹方法调用,才能让[weak]捕获生效,打破强引用链。

内容的提问来源于stack exchange,提问作者vigdora

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:57:46