在设置枚举变量时,为我的
@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...
}
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
,转而使用旧的。