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
相关产品推荐
相关产品推荐

