如果我在SwiftUI中有一个ObservableObject
,我可以将其称为@ObservedObject
:
class ViewModel: ObservableObject {
@Published var someText = "Hello World!"
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
Text(viewModel.someText)
}
}
或作为@StateObject
:
class ViewModel: ObservableObject {
@Published var someText = "Hello World!"
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
Text(viewModel.someText)
}
}
但是两者之间的实际区别是什么?有没有一种情况比另一种更好,或者它们是两种完全不同的情况?
答案 0 :(得分:34)
@ObservedObject
视图创建自己的@ObservedObject
实例时,每次丢弃并重绘视图时都会重新创建该视图:
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
}
相反,当重绘视图时,@State
变量将保留其值。
@StateObject
@StateObject
是@ObservedObject
和@State
的组合-ViewModel
的实例将被保留并重用,即使在视图被丢弃并重绘后也是如此:>
struct ContentView: View {
@StateObject var viewModel = ViewModel()
}
性能
尽管@ObservedObject
经常会被迫重新创建一个重量级的对象,但会影响性能,但是在@ObservedObject
不复杂的情况下,这无关紧要。
何时使用@ObservedObject
现在看来没有理由使用@ObserverObject
,那么什么时候应该使用它?
对于所有可观察的属性,都应使用@StateObject 在使用它的视图中初始化。如果ObservableObject实例 在外部创建并传递到使用它的视图中,标记您的 @ObservedObject属性。
请注意,可能有太多用例,有时可能需要在视图中重新创建可观察的属性。在这种情况下,最好使用@ObservedObject
。
有用的链接:
答案 1 :(得分:12)
Apple documentation确实解释了为什么用ObservedObject
初始化是不安全的。
SwiftUI可以随时创建或重新创建视图,因此,使用给定的一组输入初始化视图必须始终产生相同的视图,这一点很重要。因此,在视图内创建观察对象是不安全的。
解决方案是StateObject
。
同时,文档向我们展示了如何在视图(或应用程序/场景)中坚持真实性并将其传递给另一个视图时,如何创建数据模型。
struct LibraryView: View {
@StateObject var book = Book() // Hold on to the 1 truth
var body: some View {
BookView(book: book) // Pass it to another view
}
}
struct BookView: View {
@ObservedObject var book: Book // From external source
}
答案 2 :(得分:9)
尽管pawello2222's answer很好地解释了视图本身创建其视图模型时的差异,但重要的是要注意将视图模型注入视图时的差异。
将视图模型注入视图时,只要视图模型是引用类型,@ObservedObject
和@StateObject
之间就没有区别,因为将视图模型注入的对象您的视图也应包含对视图模型的引用,因此重绘子视图时不会破坏视图模型。
class ViewModel: ObservableObject {}
struct ParentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
ChildView(viewModel: viewModel) // You inject the view model into the child view
}
}
// Even if `ChildView` is discarded/redrawn, `ViewModel` is kept in memory, since `ParentView` still holds a reference to it - `ViewModel` is only released and hence destroyed when `ParentView` is destroyed/redrawn.
struct ChildView: View {
@ObservedObject var viewModel: ViewModel
}
答案 3 :(得分:1)
这里是一个例子。每次单击+------------+------------+---------+---------------------+---------------------+-------+
| user_uname | user_first | post_id | post_date | post_content | likes |
+------------+------------+---------+---------------------+---------------------+-------+
| user1 | Bob | 23 | 2018-12-25 17:59:46 | Merry Christmas! | 2 |
| user4 | Kate | 39 | 2018-12-26 00:28:04 | Hello! | 0 |
| user3 | Noel | 40 | 2019-01-27 00:46:12 | Hello 2 | 0 |
| user5 | Jae | 41 | 2019-02-25 00:44:35 | What are you doing? | 1 |
+------------+------------+---------+---------------------+---------------------+-------+
按钮时,refresh
都会强制销毁/重新创建StateObjectClass,这样您就可以看到CountViewObserved
,这是不期望的。
0
答案 4 :(得分:0)
@StateObject
是给定视图的状态,因此SwiftUI在body
更新中保留了它的实例。在预览中运行时,它不会保留。
@ObservedObject
只是给定View
观察到的对象,因此SwiftUI不会保留它(它必须保留在{{ 1}})。
换句话说-SwiftUI似乎保留了View
的{{1}}引用和strong
的{{1}}引用。
Retained vs non-retained source,Previews behavior source,大约在8:30左右。
答案 5 :(得分:0)
假设之间的区别:
@ObservedObject var book:BookModel 和 @StateObject var book:BookModel
@ObservedObject不拥有实例“ book”,这是您管理实例生命周期的责任。
但是,如果您想将observableObject“ book”的生命周期与@State中的视图绑定在一起,则可以使用@StateObject。 在这种情况下,SwiftUI将拥有observableObject,并且创建和销毁将与视图的生命周期相关联 SwiftUI将在视图的整个生命周期中使对象保持活动状态 这对于昂贵的资源非常有用,您无需再费心onDisappear即可释放资源
此澄清来自SwiftUI中的WWDC2020数据要点: