@StateObject
与 @ObservedObject
的区别
@StateObject
与 @ObservedObject
基础作用,这里不再解释,可看:
探讨 SwiftUI 中的关键属性包装器:@State、@Binding、@StateObject、@ObservedObject、@EnvironmentObject 和 @Environment
之前我遇到过在使用 @ObservedObject
声明 Model 后,随着 View 的刷新,Model 的数组会被清空。
具体来说可以这样表述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class People: ObservableObject { @Published var age = 0 }
struct ObservedView: View { @ObservedObject var people = People() var body: some View { VStack{ Text("\(people.age)") Button(action: {people.age = people.age + 1}) { Text("update (@ObservedObject)") } } } }
struct StateView: View { @StateObject var people = People() var body: some View { VStack{ Text("\(people.age)") Button(action: {people.age = people.age + 1}) { Text("update (@StateObject)") } } } }
struct ContentView: View { @State var count = 0 var body: some View { VStack(spacing: 50){ VStack{ Text("\(count)") Button(action: {count = count + 1} ) { Text("update") } } ObservedView() StateView() } } }
|
参考:#10 @ObservedObject的使用
在点击 ContentView 的 Button 后,ObservedView 的计数会重置,而 StateView 的计数并不会重置。等于是当更新 ContentView 的时候,原本在 ObservedView 的 people 对象又被重新生成了一次。
简单说,@ObservedObject
不管存储,会随着 View
的创建被多次创建。而 @StateObject
保证对象只会被创建一次。因此,如果是在 View
里自行创建的 ObservableObject
model 对象,大概率来说使用 @StateObject
会是更正确的选择。@StateObject
基本上来说就是一个针对 class 的 @State
升级版。
对于 View
自己创建的 ObservableObject
状态对象来说,极大概率你可能需要使用新的 @StateObject
来让它的存储和生命周期更合理:
具体可参考:@StateObject 和 @ObservedObject 的区别和使用
@Observable
参考:
- Swift 5.9 新 @Observable 对象在 SwiftUI 使用中的陷阱与解决
- SwiftUI5 新增加的Observable宏的基本用法。
- SwiftUI 属性包装器系列 — @Observable @Bindable
- 深入理解 Observation - 原理,back porting 和性能
- 深度解读 Observation —— SwiftUI 性能提升的新途径
- 新框架、新思维:解析 Observation 和 SwiftData 框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| struct Item: Identifiable { var id: UUID var value: Int init(id: UUID, value: Int) { self.id = id self.value = value } }
struct ContentView: View { @State var items: [Item] = [ Item(id: UUID(), value: 1), Item(id: UUID(), value: 2) ] @State var selectItem: Item? var body: some View { List { ForEach(items) { item in Text("\(item.value)") .onTapGesture { selectItem = item } } } .sheet(item: $selectItem) { item in ItemDetailView(item: item) } } }
struct ItemDetailView: View { @State var item: Item var body: some View { VStack() { Text("\(item.value)") Button(action: { item.value = 999 }, label: { Text("Change Value") }) } } }
|
上面代码这种情况下,无论是 ItemDetailView 还是 ContentView,其 item 的 value 都不会改变。
使用 @Observable
和 @Bindable
后可以解决问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @Observable class Item: Identifiable { var id: UUID var value: Int init(id: UUID, value: Int) { self.id = id self.value = value } }
struct ContentView: View { @State var items: [Item] = [ Item(id: UUID(), value: 1), Item(id: UUID(), value: 2) ] @State var selectItem: Item? var body: some View { List { ForEach(items) { item in Text("\(item.value)") .onTapGesture { selectItem = item print(item.value) } } } .sheet(item: $selectItem) { item in ItemDetailView(item: item) } } }
struct ItemDetailView: View { @Bindable var item: Item var body: some View { VStack() { Text("\(item.value)") Button(action: { item.value = 999 }, label: { Text("Change Value") }) } } }
|
请注意:您永远不会在 @Binding
和 @Bindable
之间进行选择。@Binding
属性包装器表明视图上的某些状态由父视图拥有,并且您对基础数据具有读写访问权限。@Bindable
表示用于创建与符合 Observable 协议的数据模型对象的可变属性的绑定;
另外,使用 SwiftData 时 @Model
与 @Bindable
配合也能实现相同的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Model class Item: Identifiable { var id: UUID var value: Int init(id: UUID, value: Int) { self.id = id self.value = value } }
struct ContentView: View { @Query private var items: [Item] @State var selectItem: Item? var body: some View { List { ForEach(items) { item in Text("\(item.value)") .onTapGesture { selectItem = item print(item.value) } } } .sheet(item: $selectItem) { item in ItemDetailView(item: item) } } }
struct ItemDetailView: View { @Bindable var item: Item var body: some View { VStack() { Text("\(item.value)") Button(action: { item.value = 999 }, label: { Text("Change Value") }) } } }
|