弱引用在作为方法引用传递时没有按预期工作

问题描述 投票:0回答:2

我已经意识到 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

swift swiftui memory-leaks weak-references retain-cycle
2个回答
4
投票

你的第一个版本:

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
返回后立即丢弃强引用。


0
投票

使用 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
返回的闭包持有另一个隐式视图模型引用。编译器无法断定显式和隐式视图模型是相关的,因此该弱点不适用于后者

© www.soinside.com 2019 - 2024. All rights reserved.