I am already aware of the strong/weak reference concept in swift.
yet after running the next code, and tapping on the Button (and dismissing the screen), the TestViewModel stayed in memory!
I was expecting that using [weak viewmodel] will be enough to prevent it.
in the second example I managed to fix it - but I don't understand why it worked
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
}
}
EXAMPLE 2:
I managed to prevent the leak by changing the code this way:
(notice the changes in the callback passed to 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()
}
}
}
}
}
I dont understand why the second TestScreen managed to apply the weak reference and the first one didn't, thanks (:
environment: swift 5 xcode 14.2
Your first version:
is equivalent to this:
SwiftUI holds on to your
@StateObjectfor as long asTestScreenis part of the view hierarchy, which is as long as theButtonis part of the view hierarchy. So SwiftUI maintains a strong reference to yourTestViewModeluntil after it has called yourButton's action. So in your first version, your weakviewmodelreference inside theButton's action will never be nil. Thereforevmwill never be nil,actionwill never be nil, andactionwill always have a strong reference to theTestViewModel.Your second version:
preserves the weakness of the
viewmodelvariable. It only creates a strong reference to theTestViewModelmomentarily, each time it is invoked, and discards the strong reference immediately aftersomeAsyncActionreturns.