弱捕获的自我不会让视图模型释放,直到任务完成

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

  1. 我提出sheet.
  2. 我拒绝 sheet。
  3. 正在调用 deinit 函数(应用日志:“已解除分配”)

案例 #2

  1. 我提出sheet.
  2. 我按下登录按钮,因此调用了 onLogin 函数。
  3. 我在 sleep(2) 结束前关闭 sheet。
  4. ---- 我希望从 deinit 打印“解除分配”消息,并在 LoggerService.log(self.isLoggingIn) 处记录打印 nilself.authenticationService.authenticate(... 从不打印被称为自我不再存在。
  5. 未预料但发生了:应用打印“正在登录”,休眠 2 秒,调用服务,打印 true,然后释放视图模型(视图在 2 秒前被关闭)

我做错了什么?

我还在学习,我不确定这是正常现象还是我错过了什么。无论如何,我希望视图模型在引用它的视图被取消时被释放。

在您调用 onLogin 时,对 self 的引用有效,因此任务开始。

之后,login 中对 self 的引用使 self 保持活动状态。 Task 有自己的生命周期,您没有取消它。

此外 sleep 的使用是错误的,因为它在任何情况下都不可取消,所以您的任务也不是。使用 Task.sleep.