警告,这是环境变量的特殊情况。我并不是说所有的@StateObject
都应该是单例的(像ViewModels)!在我看来,这是错误的。
ObservableObject
类进行设置,@Published
属性:
final class Settings: ObservableObject {
@Published var disableLockScreen = false // Just a dummy example
}
在@main
应用程序结构中,我用@StateObject
初始化它并将其注入我的视图层次结构中:
@main
struct MyTestApp: App {
@StateObject private var settings = Settings()
var body: some Scene {
MainView()
.environmentObject(settings)
}
}
所以MyTestApp
拥有Settings
的实例,所有子视图都可以通过@EnvironmentObject var settings: Settings
访问它。
OK…这对视图来说非常方便。但是…
我不需要只在视图中访问这个Settings
类。我还需要它在ViewModels不是View
结构,但ObservableObject
类。在ViewModel中,我可能想在代码部分访问Settings
的属性(不直接链接到视图)。例如,我有UIApplication.shared.isIdleTimerDisabled = settings.disableLockScreen
.
让我的ViewModels访问Settings
,我必须在它们的init中注入它。就像这里和这里解释的那样。当然,这是可能的,但说实话,它生成的代码不多,而且阅读起来要复杂得多。
所以我找到了这个公认的答案,它显示了一个存储在@StateObject
中的Singleton示例。我当时正在考虑改变我的Settings
类是一个单例。在MyTestApp
中,我只使用Settings.shared
更改Settings()
以在应用程序中存储Singleton实例,并继续将其提供给子视图:
@main
struct MyTestApp: App {
@StateObject private var settings = Settings.shared
var body: some Scene {
MainView()
.environmentObject(settings)
}
}
真正变得更好的地方,是在ViewModels:他们现在可以简单地访问Settings.shared
实例,如果他们需要访问它的属性。不需要大量代码的依赖注入。
在我看来,它真的很简单,流畅和正确。但我一点也不确定。使用Singleton作为在环境中共享的@StateObject
是一个好方法吗?有什么我没看到的可能会引起问题的地方吗?是否所有@StateObject
共享到环境中并由View
以外的人使用的都应该是singleton ?
当您需要将视图状态作为引用类型对象时,请将@StateObject
视为@State
,现在我们有.task
,这应该不经常。此外,您通常不会在App
结构体中使用@StateObject
,因为当没有使用其属性时,每次更改都会不必要地重新计算主体。State(在这两种情况下)声明了一个真实的状态,你永远不会传递可能改变的参数到它们的初始化,因为它们没有办法监视变化。
对于您的Settings
,如果它只是一些简单的变量,那么它可以是一个结构体,您可以使用自定义的EnvironmentKey
使其无处不在。如果您需要访问自定义结构中的设置(声明为@State var customStruct = CustomStruct
),则使其符合DynamicProperty
并在func update()
中访问它。如果@Environment发生变化,就会调用update
,与View
调用body的方式相同。您可以将任何其他相关@State
或@StateObject
移动到DynamicProperty
中。例如
struct CustomStruct: DynamicProperty {
@Environment(.settings) var settings
@State var ...
@StateObject var ...
func update() {
// environment or state values changed
}
}
如果设置是保存模型数据,你需要一个引用类型,例如,你正在做一些异步加载或保存,那么你可以使用一个单例并传递它作为environmentObject
,例如
@main
struct MyTestApp: App {
var body: some Scene {
MainView()
.environmentObject(Settings.shared)
}
}
和预览
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(Settings.preview)
}
}