SwiftUI中的ObservedObject和StateObject有什么区别

时间:2020-06-23 21:34:23

标签: ios swift swiftui

如果我在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)
    }
}

但是两者之间的实际区别是什么?有没有一种情况比另一种更好,或者它们是两种完全不同的情况?

6 个答案:

答案 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 sourcePreviews 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数据要点: