@ObservedObject:是什么,观察对象并更新View

作者: IT Sectr 发布日期: 2026-06-26 阅读时间: 9 分钟

@ObservedObject 是SwiftUI中的一个属性包装器,允许View观察在层次结构其他位置创建的ObservableObject的变化。与@StateObject不同,@ObservedObject不会创建对象—它只订阅其objectWillChange发布者,并在@Published属性更新时重新绘制View。这使得@ObservedObject成为通过初始化器从父级接收数据的子View的正确选择。根据Paul Hudson的文章—Hacking with Swift (2025),典型的SwiftUI应用程序架构构建如下:根View使用@StateObject创建view model,所有子View通过@ObservedObject接收它,从而确保单一数据源而无需重复数据。

主要内容

  • @ObservedObject —用于观察在父View中创建的ObservableObject的属性包装器。
  • 不拥有对象 —与@StateObject不同,@ObservedObject不管理对象的生命周期。
  • 订阅变化 —当@Published属性变化时,View自动重新绘制。
  • 通过初始化器传递 —对象通过初始化器参数传递给子View。
  • iOS 13+ —@ObservedObject从SwiftUI的第一个版本起可用,与@StateObject不同(iOS 14+)。

SwiftUI中的@ObservedObject是什么

@ObservedObject 是一个属性包装器,它将View订阅到ObservableObject的变化。当标记有@ObservedObject的对象更改其任何用@Published声明的属性时,SwiftUI会自动重新绘制View。@ObservedObject不会创建对象—它只在现有的ObservableObject实例和需要对其变化做出反应的View之间建立连接。

@ObservedObject 和 @StateObject 之间的关键区别—是所有权。@ObservedObject假定对象是在View层次结构中更高的位置创建和存储的。子View通过初始化器获取此对象的引用并简单地观察它。如果子View被重新创建,它将从父级获得相同的引用—数据不会丢失。

根据Apple Developer Documentation — SwiftUI (2025),@ObservedObject从iOS 13起可用,这使其成为支持旧版iOS的项目中观察ObservableObject的唯一选项。在iOS 14+中,创建对象更推荐使用@StateObject,但@ObservedObject仍然适用于传递现有对象。

@ObservedObject如何工作

@ObservedObject 的机制基于Combine框架中的ObservableObject协议。每个符合ObservableObject的类都会自动获得一个objectWillChange发布者,该发布者在任何@Published属性更改之前发送信号。SwiftUI通过@ObservedObject订阅此发布者,并在收到信号后将View标记为需要重新绘制。

swift
class TaskViewModel: ObservableObject {
    @Published var tasks: [Task] = []
    @Published var isLoading = false
    
    func loadTasks() async {
        isLoading = true
        // 获取数据
        isLoading = false
    }
}

struct TaskListView: View {
    @ObservedObject var viewModel: TaskViewModel
    
    var body: some View {
        List(viewModel.tasks) { task in
            Text(task.title)
        }
        .task { await viewModel.loadTasks() }
    }
}

当父View通过初始化器将viewModel传递给TaskListView时,SwiftUI在对象和View之间建立连接。当tasks数组或isLoading标志更改时,SwiftUI重新绘制TaskListView。对象保持不变—它通过@StateObject存储在父View中。

@ObservedObject vs @StateObject:何时使用什么

@ObservedObject 和 @StateObject 之间的区别—是观察者和所有者之间的区别。@StateObject创建对象并管理其生命周期。@ObservedObject只观察在其他地方创建和存储的对象。它们之间的选择由View对数据的责任决定。

场景建议原因
View创建数据@StateObjectView拥有对象并负责其生命周期
View接收数据@ObservedObjectView只观察,对象存在于父级中
支持iOS 13@ObservedObject@StateObject不可用,使用@ObservedObject并在手动管理
可复用组件@ObservedObject组件不应创建数据—它从外部接收数据

主要规则:如果View创建对象—@StateObject。如果View接收对象—@ObservedObject。违反此规则使用@ObservedObject创建对象会导致View重建时数据丢失。违反规则使用@StateObject接收对象会创建一个独立的重复实例,与父级无关。

@ObservedObject使用示例

@ObservedObject 的典型使用场景—任务列表,其中根View创建view model,列表单元格通过@ObservedObject接收它。每个单元格都可以调用view model的方法,更改会自动显示在整个列表中,因为所有单元格都观察相同的对象。

swift
struct TaskRow: View {
    @ObservedObject var viewModel: TaskViewModel
    let task: Task
    
    var body: some View {
        HStack {
            Text(task.title)
            Spacer()
            Button("完成") {
                viewModel.completeTask(task)
            }
        }
    }
}

struct TaskListContainer: View {
    @StateObject var viewModel = TaskViewModel()
    
    var body: some View {
        List(viewModel.tasks) { task in
            TaskRow(viewModel: viewModel, task: task)
        }
    }
}

在此示例中,TaskListContainer通过@StateObject创建viewModel,每个TaskRow通过@ObservedObject接收它。当用户在任何行中点击“Done”时,viewModel.completeTask更改published属性,所有观察此对象的View都会自动更新。

@ObservedObject的常见错误

最常见的错误—在View内部使用@ObservedObject创建对象。当View重建时(例如在状态改变时),SwiftUI会创建一个新的ObservableObject实例,导致所有累积数据丢失。这个错误在NavigationStack中尤其痛苦,用户可能填写表单并在返回时丢失数据。

错误:使用@ObservedObject代替@StateObject

swift
// ❌ 数据丢失:@ObservedObject不保留对象
struct FormView: View {
    @ObservedObject var formVM = FormViewModel()
    // 每次View重建时都会创建新的formVM!
}

// ✅ 正确:@StateObject保留对象
struct FormView: View {
    @StateObject var formVM = FormViewModel()
    // 对象在View生命周期内创建一次
}

错误:在需要@ObservedObject的地方传递@StateObject

如果子View通过@StateObject声明相同的ObservableObject,它会创建一个独立的副本。父对象中的更改将不会在子对象中可见,反之亦然。始终为从外部接收对象的子View使用@ObservedObject

SwiftUI中@ObservedObject的替代方案

在现代SwiftUI中,有几种@ObservedObject的替代方案,各有其优势。选择取决于应用程序架构、iOS版本和特定的使用场景。

  • @EnvironmentObject —允许从SwiftUI环境中获取对象,无需通过初始化器显式传递。适用于许多屏幕都需要的对象,但需要通过.environmentObject()显式注入。
  • @State + @Binding —对于简单的值类型不需要ObservableObject。使用@State进行存储,使用@Binding传递给子View。
  • @AppStorage —用于需要与View自动同步的UserDefaults值。
  • @SceneStorage —用于在场景重启之间保存临时状态(例如列表中的位置)。

@ObservedObject和@EnvironmentObject之间的选择—是风格和架构的问题。@ObservedObject通过初始化器显式显示View的依赖关系,使代码更可预测。@EnvironmentObject适用于深层层次结构,但隐藏依赖关系,可能会使调试变得困难。

常见问题

可以在没有父级@StateObject的情况下使用@ObservedObject吗?

可以,如果对象是在SwiftUI之外创建和存储的—例如,在AppDelegate单例中。在这种情况下,@ObservedObject只是订阅现有对象的更改。然而,对于在SwiftUI层次结构内创建的对象,始终需要某处更高位置的@StateObject。

为什么@ObservedObject有时不更新View?

最可能的原因—属性不是通过@Published更改的,或者不是对象本身更改,而是其内部结构在没有调用objectWillChange的情况下更改。对于集合,使用新副本的赋值:array.append()是不够的—需要通过array = array + [element]重新分配整个数组。

@ObservedObject会影响性能吗?

@ObservedObject本身不会造成显著负担。问题出现在频繁更改@Published属性时—每次更改都会触发所有观察View的重新绘制。为了优化,使用EquatableView,减少published属性的数量,避免不必要的更新。

@ObservedObject和@Binding有什么区别?

@ObservedObject 观察整个ObservableObject类,并在其published属性发生任何更改时重新绘制View。@Binding 创建与特定值(String、Int、Bool)的双向连接,并允许读取和写入它。@Binding更轻量,不需要ObservableObject。

@ObservedObject和@Published可以在同一个类中组合使用吗?

可以,这是标准模式。ObservableObject内部的@Published会自动与@ObservedObject集成。每个@Published属性都会向objectWillChange发布者添加一个观察者。当其中任何一个更改时,所有带有@ObservedObject的该对象的View都会重新绘制。

总结

  • @ObservedObject —用于观察在层次结构其他位置创建的ObservableObject的属性包装器。
  • 不拥有对象 —与@StateObject不同,@ObservedObject不管理生命周期也不创建对象。
  • 通过Combine订阅 —SwiftUI自动订阅ObservableObject的objectWillChange发布者。
  • iOS 13+ —@ObservedObject从SwiftUI的第一个版本起可用,对于具有旧支持的项目很重要。
  • 通过初始化器传递 —对象显式传递给子View,使依赖关系透明。
  • 所有权错误 —使用@ObservedObject创建对象会导致View重建时数据丢失。
  • 替代方案 —@EnvironmentObject用于通过环境注入,@State/@Binding用于值类型。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读