SwiftUI @StateObject, @ObservedObject, @Observable 三者的区别

@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

参考:

  1. Swift 5.9 新 @Observable 对象在 SwiftUI 使用中的陷阱与解决
  2. SwiftUI5 新增加的Observable宏的基本用法。
  3. SwiftUI 属性包装器系列 — @Observable @Bindable
  4. 深入理解 Observation - 原理,back porting 和性能
  5. 深度解读 Observation —— SwiftUI 性能提升的新途径
  6. 新框架、新思维:解析 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")
})
}
}
}

SwiftUI @StateObject, @ObservedObject, @Observable 三者的区别
https://wonderhoi.com/2024/02/18/SwiftUI-StateObject-ObservedObject-Observable-三者的区别/
作者
wonderhoi
发布于
2024年2月18日
许可协议