SwiftUI架构:值类型与引用类型及@State/@ObservedObject对比
SwiftUI中@State(值类型)与@ObservedObject(引用类型)的架构选择问题
暂不考虑值类型与引用类型本身的选择理由,仅从SwiftUI架构层面出发,探讨其更偏好使用@State还是@ObservedObject?
以下是一个基础列表应用的两种实现方案:猫采用struct(值类型)实现,狗采用class(引用类型)实现:
import SwiftUI struct Cat: Identifiable { let id = UUID() var name: String = "New cat" } final class Dog: Identifiable, ObservableObject { let id = UUID() @Published var name: String = "New dog" } final class Dogs: ObservableObject { @Published var dogs: [Dog] = [] } struct ContentView: View { @State private var cats: [Cat] = [] @ObservedObject private var dogs = Dogs() var body: some View { VStack { Text("Cats").bold() ForEach($cats, content: CatView.init) Button("New cat") { cats.append(Cat()) } Divider().padding() Text("Dogs").bold() ForEach(dogs.dogs, content: DogView.init) Button("New dog") { dogs.dogs.append(Dog()) } } } } struct CatView: View { @Binding var cat: Cat var body: some View { TextField("Name", text: $cat.name) } } struct DogView: View { @ObservedObject var dog: Dog var body: some View { TextField("Name", text: $dog.name) } }
两种实现的代码差异极小,针对以下问题逐一解答:
1. 是否存在偏好其中一种方案的理由?苹果官方示例常采用struct + @State + @Bindings组合。
苹果官方确实更偏好struct + @State + @Binding的组合,核心原因贴合SwiftUI的设计哲学:
- SwiftUI视图本身是值类型,值类型数据模型能让状态流转更可预测——值类型的复制修改特性,让每一次状态变化都生成新实例,SwiftUI可精准捕捉变化并触发视图更新,避免引用类型可能出现的状态模糊问题。
- 这种组合代码更简洁,无需额外定义
ObservableObject子类和@Published属性,减少样板代码。 - 架构一致性更强,值类型模型与SwiftUI视图体系天然适配,无需关注
ObservedObject的生命周期管理,降低内存泄漏或异常更新的风险。
2. 两种选择是否存在性能差异?比如当Cat或Dog对象包含大量字段(ObservedObject可精确指定触发objectWillChange调用的属性),或者数组包含数十万个元素时。
存在明确的性能差异,分场景来看:
- 单对象多字段场景:若模型包含大量字段,
ObservedObject通过@Published可精准控制触发视图更新的属性;而值类型的@State/@Binding只要模型任意字段修改,就会生成新实例,SwiftUI需对比新旧实例差异决定是否更新——虽SwiftUI的差异对比优化出色,但极端场景(如上百个字段频繁修改)下,引用类型的精准更新效率更高。 - 大数据数组场景:当数组包含数十万个元素时,值类型数组的单个元素修改会触发数组写时复制(仅修改元素复制,但数组引用变化),
@State会触发父视图更新,进而让ForEach重新计算所有子视图差异;而引用类型数组中,单个Dog对象的属性修改仅会让对应DogView更新,父视图不会因数组本身无变化而刷新,性能优势明显。但数组增删操作时,两者性能差异不大,都会触发数组变化通知。
3. 既然Bindings可为值类型赋予伪引用特性,那么何时应选择使用引用类型?
即便@Binding能让值类型拥有类引用的修改能力,以下场景仍更适合引用类型:
- 跨多视图共享同一状态实例:多个不相关视图需修改同一份数据,且不想通过层层传递
Binding实现——引用类型的ObservableObject可作为全局或环境对象,直接在多视图中访问修改,避免Binding链过长带来的代码复杂度。 - 模型需持有可变引用类型资源:比如模型要管理网络请求任务、Core Data上下文、定时器等引用类型对象,值类型复制后会生成新引用,无法维持对原资源的持有,此时必须用引用类型模型。
- 复杂状态的精细化更新控制:当模型状态变化逻辑复杂,需精准控制视图更新时机(如部分属性变化无需通知视图),
ObservableObject可手动调用objectWillChange.send()控制更新,比値类型的自动更新更灵活。 - 与现有引用类型API交互:若项目需与UIKit、Core Data等基于引用类型的框架深度集成,使用引用类型模型能减少适配成本。
内容的提问来源于stack exchange,提问作者Philip Pegden
相关产品推荐
相关产品推荐

