提问人:vigdora 提问时间:4/13/2023 最后编辑:vigdora 更新时间:4/18/2023 访问量:453
弱引用在作为方法引用传递时无法按预期工作
Weak reference doesn't work as expected when passing it as a method reference
问:
我已经知道 swift 中的强/弱引用概念。
然而,在运行下一个代码并点击 Button(并关闭屏幕)后,TestViewModel 仍保留在内存中!
我原以为使用 [弱视图模型] 就足以防止它。
在第二个示例中,我设法修复了它 - 但我不明白它为什么有效
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)
只要是视图层次结构的一部分,SwiftUI 就会保留你,只要它是视图层次结构的一部分。因此,SwiftUI 会一直保持对 your 的强烈引用,直到它调用了 your 的 action。因此,在你的第一个版本中,你在 的动作中的弱引用永远不会为零。因此,永远不会为零,永远不会为零,并且始终对 .@StateObject
TestScreen
Button
TestViewModel
Button
viewmodel
Button
vm
action
action
TestViewModel
您的第二个版本:
testStruct.saveActionGlobaly(onAsyncAction: { await viewmodel?.someAsyncAction() } )
保留变量的弱点。它只会在每次调用时创建对瞬时的强引用,并在返回后立即丢弃强引用。viewmodel
TestViewModel
someAsyncAction
评论
viewmodel?.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
}
并将其交给争论。
换言之,从 Evaluating 返回的闭包包含另一个隐式 ViewModel 引用。编译器无法断定显式视图模型和隐式视图模型是相关的,因此该弱点不适用于后者onAsyncAction
action
评论
View
.task