@StateObject和@ObservedObject有什么区别?

由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/

你可能感兴趣的:(iOS移动开发,StateObject)