弱捕获的自我不会让视图模型释放,直到任务完成
Weakly captured self won't let the view model deallocate until the Task finishes
我正在努力学习 ARC,但我很难接受一个弱势的自我。我的项目使用 MVVM 和 SwiftUI。我正在展示一个具有 @StateObject var viewModel = AuthenticationLoginViewModel()
属性 的 sheet (AuthenticationLoginView)。在关闭呈现的 sheet 时,我希望 viewModel 会调用它 deinit
并且它会调用 until我 运行 Task
块中的异步函数。
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)
}
}
所以我有两个案例:
案例 #1
- 我提出sheet.
- 我拒绝 sheet。
- 正在调用
deinit
函数(应用日志:“已解除分配”)
案例 #2
- 我提出sheet.
- 我按下登录按钮,因此调用了
onLogin
函数。
- 我在
sleep(2)
结束前关闭 sheet。
- ---- 我希望从
deinit
打印“解除分配”消息,并在 LoggerService.log(self.isLoggingIn)
处记录打印 nil
和 self.authenticationService.authenticate(...
从不打印被称为自我不再存在。
- 未预料但发生了:应用打印“正在登录”,休眠 2 秒,调用服务,打印 true,然后释放视图模型(视图在 2 秒前被关闭)
我做错了什么?
我还在学习,我不确定这是正常现象还是我错过了什么。无论如何,我希望视图模型在引用它的视图被取消时被释放。
在您调用 onLogin
时,对 self
的引用有效,因此任务开始。
之后,login
中对 self
的引用使 self
保持活动状态。 Task 有自己的生命周期,您没有取消它。
此外 sleep
的使用是错误的,因为它在任何情况下都不可取消,所以您的任务也不是。使用 Task.sleep
.
我正在努力学习 ARC,但我很难接受一个弱势的自我。我的项目使用 MVVM 和 SwiftUI。我正在展示一个具有 @StateObject var viewModel = AuthenticationLoginViewModel()
属性 的 sheet (AuthenticationLoginView)。在关闭呈现的 sheet 时,我希望 viewModel 会调用它 deinit
并且它会调用 until我 运行 Task
块中的异步函数。
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)
}
}
所以我有两个案例:
案例 #1
- 我提出sheet.
- 我拒绝 sheet。
- 正在调用
deinit
函数(应用日志:“已解除分配”)
案例 #2
- 我提出sheet.
- 我按下登录按钮,因此调用了
onLogin
函数。 - 我在
sleep(2)
结束前关闭 sheet。 - ---- 我希望从
deinit
打印“解除分配”消息,并在LoggerService.log(self.isLoggingIn)
处记录打印nil
和self.authenticationService.authenticate(...
从不打印被称为自我不再存在。 - 未预料但发生了:应用打印“正在登录”,休眠 2 秒,调用服务,打印 true,然后释放视图模型(视图在 2 秒前被关闭)
我做错了什么?
我还在学习,我不确定这是正常现象还是我错过了什么。无论如何,我希望视图模型在引用它的视图被取消时被释放。
在您调用 onLogin
时,对 self
的引用有效,因此任务开始。
之后,login
中对 self
的引用使 self
保持活动状态。 Task 有自己的生命周期,您没有取消它。
此外 sleep
的使用是错误的,因为它在任何情况下都不可取消,所以您的任务也不是。使用 Task.sleep
.