SwiftUI @Observable 类 ViewModel 在设置枚举变量时被初始化两次,但不是类 ViewModel: ObservableObject

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

在设置枚举变量时,为我的

@Observable class ViewModel
使用新的观察框架时,我的 ViewModel 被初始化两次时出现问题。尝试将其改回较旧的
class ViewModel: ObservableObject
,一切都按预期进行。有没有人解释一下?

这个例子不起作用。当在

ViewModel.init()
上使用
AuthManager.init()
宏时设置
ViewModel.loginStatus
枚举时,
@Observable
ViewModel
会被调用两次:

import SwiftUI

struct ContentView: View {
   @State private var vm = ViewModel()

   var body: some View {
      Button {
         vm.login()
      } label: {
         Text("Login")
      }
      switch vm.loginStatus {
      case .unknown, .loggedOut:
         Text("Login Screen")
      case .loggedIn:
         Text("Home Screen")
      }
   }
}

-----------------------------------------------

import Foundation
import Observation

@Observable class ViewModel {
   private let auth = AuthManager()

   enum LoginStatus {
      case unknown, loggedIn, loggedOut
   }
   var loginStatus = .unknown

   init() {
      print(#function)
      auth.delegate = self
   }
   
   func login() {
      auth.login()
   }
}

extension ViewModel: AuthManagerDelegate {
   func authStateDidChange(isLoggedIn: Bool) {
      logginStatus = isLoggedIn ? .loggedIn : .loggedOut
   }
}

-----------------------------------------------

protocol AuthManagerDelegate: AnyObject {
   func authStateDidChange(isLoggedIn: Bool)
}

class AuthManager {
   weak var delegate: AuthManagerDelegate?
   private let auth = Dependency()

   init() {
      print(#function)
   }

   var user: User? {
      didSet {
         delegate?.authStateDidChange(isLoggedIn: user != nil)
      }
   }

   func login() {
      auth.signIn { [weak self] result in
         guard let self else { return }
         user = result.user
      }
   }
}

但是,在使用

@Observable
宏之前将其转换回旧方式时,
ViewModel.init()
AuthManager.init()
仅按预期被调用一次:

struct ContentView: View {
   @StateObject private var vm = ViewModel()

   var body: some View {
      ...same as above...
   }
}

-----------------------------------------------

class ViewModel: ObservableObject {
   private let auth = AuthManager()

   enum LoginStatus {
      case unknown, loggedIn, loggedOut
   }
   @Published var loginStatus = .unknown

   ini() {
      print(#function)
      auth.delegate = self
   }

   func login() {
      auth.login()
   }
}

extension ViewModel: AuthManagerDelegate {
   func authStateDidChange(isLoggedIn: Bool) {
      logginStatus = isLoggedIn ? .loggedIn : .loggedOut
   }
}

-----------------------------------------------

protocol AuthManagerDelegate: AnyObject {
   ...same as above...
}

class AuthManager {
   ...same as above...
}
swiftui enums observableobject observation
1个回答
0
投票

这是一个不幸的历史事故(我相信)

State
初始化器有这个签名

init(wrappedValue value: Value)

StateObject
初始化器具有此签名

init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType)

请注意,

StateObject
需要
@autoclosure
,而
State
则不需要。

因此,每次您的程序创建旧式(基于

StateObject
ContentView
的实例时,它都会使用调用
StateObject
初始化器的闭包来初始化
vm
包装的
ViewModel
属性。 SwiftUI 仅在
ContentView
第一次出现在视图层次结构中时调用该闭包一次。在后续更新中,它会重用已创建的
ViewModel
,而不是调用闭包来创建另一个。

但是每次您的程序创建新样式(基于

Observation
ContentView
的实例时,它都会使用新创建的
State
来初始化
vm
包装的
ViewModel
属性。然后,在更新时,SwiftUI 会丢弃新的
ViewModel
,转而使用旧的。

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