我已经意识到 swift 中的强/弱引用概念。
然而在运行下一段代码并点击按钮(并关闭屏幕)后,TestViewModel 仍保留在内存中!
我期待使用 [weak viewmodel] 足以防止它。
在第二个例子中,我设法修复了它——但我不明白为什么它会起作用
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()
}
}
}
}
}
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:
我设法通过这种方式更改代码来防止泄漏:
(注意传递给 onAsyncAction 的回调的变化)
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()
}
}
}
}
}
我不明白为什么第二个 TestScreen 设法应用弱引用而第一个没有, 谢谢(:
环境: 斯威夫特5 xcode 14.2
你的第一个版本:
testStruct.saveActionGlobaly(onAsyncAction: viewmodel?.someAsyncAction )
相当于:
let action: (() async throws -> Void)?
if let vm = viewmodel {
// vm is a strong non-nil reference, so this closure
// has a strong non-nil reference to a TestViewModel.
action = vm.someAsyncAction
} else {
action = nil
}
testStruct.saveActionGlobaly(onAsyncAction: action)
只要 @StateObject
是视图层次结构的一部分,SwiftUI 就会保留您的
TestScreen
,只要 Button
是视图层次结构的一部分。因此,SwiftUI 会保持对您的 TestViewModel
的强引用,直到它调用了您的 Button
的操作。因此,在您的第一个版本中,您在 viewmodel
动作中的弱 Button
引用永远不会为零。因此 vm
永远不会为零,action
永远不会为零,并且 action
将始终强烈引用 TestViewModel
.
你的第二个版本:
testStruct.saveActionGlobaly(onAsyncAction: { await viewmodel?.someAsyncAction() } )
保留了
viewmodel
变量的弱点。它只会在每次调用时立即创建对TestViewModel
的强引用,并在someAsyncAction
返回后立即丢弃强引用。
使用 rob 的回答并做一些额外的阅读,我想我设法对此有了更多的了解(至少对我来说,因为 rob 的回答当然是正确的):
首先,弱引用概念是一个编译器游戏 - 意思是,编译器运行第一个示例并将其翻译为:(如 rob 所述)
let action: (() async throws -> Void)?
if let vm = viewmodel {
// vm is a strong non-nil reference, so this closure
// has a strong non-nil reference to a TestViewModel.
action = vm.someAsyncAction
} else {
action = nil
}
testStruct.saveActionGlobaly(onAsyncAction: action)
有道理...
对我来说,缺少的部分是理解 rob 的下一句话:
action 将始终强烈引用 TestViewModel
所以,还有一个步骤,编译器将
action
翻译成这样的闭包(非常抽象):
{
viewmodel.action // implicit viewmodel
}
并将其交给
onAsyncAction
论点。
换句话说,评估 action
返回的闭包持有另一个隐式视图模型引用。编译器无法断定显式和隐式视图模型是相关的,因此该弱点不适用于后者