Reputation: 1621
I am trying to learn ARC and I'm having a hard time with a weakly captured self. My project is using MVVM with SwiftUI. I'm presenting a sheet (AuthenticationLoginView) that has a @StateObject var viewModel = AuthenticationLoginViewModel()
property. On dismiss of the presented sheet, I expect that the viewModel will have it's deinit
called and so it does until I run an asynchronous function within a Task
block.
class AuthenticationLoginViewModel: ObservableObject {
@Published var isLoggingIn: Bool = false
private var authenticationService: AuthenticationService
private var cancellables: Set<AnyCancellable> = Set()
private var onLoginTask: Task<Void, Never>?
init(authenticationService: AuthenticationService) {
self.authenticationService = authenticationService
}
deinit {
onLoginTask?.cancel()
LoggerService.log("deallocated")
}
public func onLogin() {
guard !isLoggingIn else { return }
isLoggingIn = true
onLoginTask = Task { [weak self] in
await self?.login()
}
}
private func login() async {
LoggerService.log("Logging in...")
sleep(2)
//
// self is still allocated here <<<---- ???
//
let authResponse = try? await self.authenticationService.authenticate(username: username, password: password)
LoggerService.log(self.isLoggingIn) // <<--- prints `true`
handleLoginResponse(authResponse: authResponse)
}
}
So I have my two cases here:
deinit
function is getting called (app logs: "deallocated")onLogin
function is getting called.sleep(2)
ends.deinit
and the logging at LoggerService.log(self.isLoggingIn)
to print nil
and the self.authenticationService.authenticate(...
to never be called as self doesn't exist anymore.What am I doing wrong?
I'm still learning and I'm pretty much unsure if this is normal or I miss something. Anyway, I expect the view model to be deallocated as the view referencing it was dismissed.
Upvotes: 0
Views: 425
Reputation: 535149
At the time you call onLogin
the reference to self
is valid and so the Task commences.
After that, the reference to self
in login
keeps self
alive. The Task has a life of its own, and you did not cancel it.
Moreover the use of sleep
is wrong, as it is not cancellable in any case, so neither is your Task. Use Task.sleep
.
Upvotes: 2