由donnywals于2020年6月23日发布
SwiftUI中的视图将被丢弃并定期重新创建。 发生这种情况时,将重新初始化整个视图结构。 因此,除非您已使用@State标记了这些值,否则您在SwiftUI视图中创建的任何值都将重置为其默认值。
这意味着,如果您声明一个创建自己的@ObservedObject实例的视图(@ObservedObject的示例位于《SwiftUI从入门到实战》课程第六章第11节),则每当SwiftUI决定需要丢弃并重画该视图时,该实例都会被替换。
如果您想了解我的意思,请尝试运行以下SwiftUI视图:
class DataSource: ObservableObject {
@Published var counter = 0
}
struct Counter: View {
@ObservedObject var dataSource = DataSource()
var body: some View {
VStack {
Button("Increment counter") {
dataSource.counter += 1
}
Text("Count is \(dataSource.counter)")
}
}
}
struct ItemList: View {
@State private var items = ["hello", "world"]
var body: some View {
VStack {
Button("Append item to list") {
items.append("test")
}
List(items, id: \.self) { name in
Text(name)
}
Counter()
}
}
}
尽管此示例中的视图可能不是超级有用,但它很好地演示了Counter如何创建自己的@ObservedObject。如果您多次点击“计数器”中定义的“递增计数器”按钮,您会发现其“计数为...”标签每次都会更新。如果您然后单击ItemList中定义的“将项追加到列表”按钮,则Counter中的Count is ...标签将重置为0。其原因是重新创建了Counter,这意味着我们现在有了DataSource的新实例。
要解决此问题,我们可以在ItemList中创建DataSource,将该实例保留为ItemList上的一个属性,然后将该实例传递给Counter,或者我们可以使用@StateObject(@StateObject的互动示例位于《SwiftUI从入门到实战》课程第六章第13节)而不是@ObservedObject:
struct Counter: View {
@StateObject var dataSource = DataSource()
var body: some View {
VStack {
Button("Increment counter") {
dataSource.counter += 1
}
Text("Count is \(dataSource.counter)")
}
}
}
通过将DataSource设置为@StateObject,我们创建的实例将保留并在重新创建Counter视图时使用。这非常有用,因为ItemList不必代表Counter保留DataSource,这使DataSource更加整洁。
您应该将@StateObject用于在保留该对象的对象中创建的任何ObservableObject属性。因此,在这种情况下,Counter创建自己的DataSource,这意味着如果我们要保留它,则必须将其标记为@StateObject。
如果视图在其初始值设定项中接收到ObservableObject,则可以使用@ObservedObject,因为该视图不会自行创建该实例:
struct Counter: View {
// the DataSource must now be passed to Counter's initializer
@ObservedObject var dataSource: DataSource
var body: some View {
VStack {
Button("Increment counter") {
dataSource.counter += 1
}
Text("Count is \(dataSource.counter)")
}
}
}
请记住,尽管这不能在所有情况下都解决问题。如果创建Counter的对象(或具有@ObservedObject的视图)没有保留ObservableObject,则每次该视图重绘其主体时都会创建一个新实例:
struct ItemList: View {
@State private var items = ["hello", "world"]
var body: some View {
VStack {
Button("Append item to list") {
items.append("test")
}
List(items, id: \.self) { name in
Text(name)
}
// a new data source is created for every redraw
Counter(dataSource: DataSource())
}
}
}
但是,这并不意味着您应该将所有@ObservedObject属性标记为@StateObject。在后一种情况下,每次重新绘制视图时,ItemList的意图可能是创建一个新的DataSource。如果您将Counter.dataSource标记为@StateObject,则新实例将被忽略,您的应用程序现在可能会有一个新的隐藏错误。
@StateObject并非完全不重要的含义是性能。如果您使用的是经常重新创建的@ObservedObject,则可能会损害视图的渲染性能。由于不会为每个重新渲染的视图重新创建@StateObject,因此对视图的绘制周期的影响要小得多。当然,对一个小对象的影响可能很小,但是如果您的@ObservedObject更复杂,则影响可能会迅速增长。
简而言之,对于在使用它的视图中初始化的所有可观察属性,都应使用@StateObject。如果ObservableObject实例是在外部创建的,并传递给使用它的视图,请使用@ObservedObject标记您的属性。
译自:https://www.donnywals.com/whats-the-difference-between-stateobject-and-observedobject/