@ObservedObject:什么是它,如何工作及示例

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

@ObservedObject 是 SwiftUI 中的一个属性包装器,用于观察从外部传入的 ObservableObject 实例。与 @StateObject 不同,@ObservedObject 不会创建对象 — 它订阅现有对象的更改。根据 Apple Developer Documentation (2025),@ObservedObject 用于需要跟踪属于父级数据的子视图。@ObservedObject 提供无需管理对象生命周期的响应式连接。

要点

  • @ObservedObject — 用于观察 ObservableObject 而不拥有它的属性包装器
  • 无需创建 — 对象从父视图或环境中传入
  • @Published — ObservableObject 内部的属性,其更改由 SwiftUI 跟踪
  • 重新绘制 — 当 @Published 属性更改时,SwiftUI 更新所有订阅的视图
  • 不要与 @StateObject 混淆 — @ObservedObject 不保证唯一实例

SwiftUI 中的 @ObservedObject 是什么?

@ObservedObject 是一个属性包装器,它将视图订阅到 ObservableObject 的更改。ObservableObject 是来自 Combine 框架的协议,需要实现 objectWillChange 发布器。当 ObservableObject 内部任何标记为 @Published 的属性发生更改时,发布器发送信号,SwiftUI 重新绘制所有通过 @ObservedObject 订阅的视图。

@ObservedObject 的主要特点是不拥有对象。视图不负责创建或销毁对象。对象在父视图中创建(通过 @StateObject)或通过 @EnvironmentObject 注入。子视图仅观察更改并接收更新。如果对象在父级中被替换,@ObservedObject 会切换到新实例。

@ObservedObject 适用于数据共享场景:用户模型、共享设置、服务器连接状态。当层次结构中不同级别的多个视图需要显示相同数据时,每个视图中的 @ObservedObject 会在一个源上创建独立但协调一致的订阅。

@ObservedObject vs @StateObject:关键区别

@ObservedObject 和 @StateObject 之间的区别是 SwiftUI 面试中最常见的话题之一。基本规则:@StateObject 创建并拥有对象,@ObservedObject 观察现有对象。违反此规则会导致意外数据丢失或双重初始化。

特性@StateObject@ObservedObject
创建对象是,在视图初始化时否,接收现成的
拥有权当前视图父组件
唯一实例是,在整个生命周期中否,可能被替换
渲染时重新创建否,保持不变取决于父级
使用场景根视图-拥有者子视图

@StateObject 保证对象只创建一次,并在视图结构的重复初始化中幸存。@ObservedObject 从外部接收对象,并在每次父结构初始化时重新创建。如果父级使用 @StateObject 创建对象,子视图可以安全地使用 @ObservedObject — 对象在整个层次结构中是唯一的。

@ObservedObject 如何跟踪变化

@ObservedObject 的跟踪机制基于 Combine 和 ObservableObject 协议。在初始化时,SwiftUI 调用 objectWillChange 发布器 — 对象必须在 @Published 属性更改之前发出信号。Combine 将信号传递到 SwiftUI 的依赖图中,该图将所有依赖视图标记为需要更新。这在值更改之前同步发生。

swift
class WeatherService: ObservableObject {
    @Published var temperature: Double = 22.0
    @Published var city: String = "Moscow"
}

struct WeatherView: View {
    @ObservedObject var weather: WeatherService

    var body: some View {
        VStack {
            Text("\\(weather.city)")
            Text("\\(weather.temperature)°C")
        }
    }
}

在代码片段中,WeatherService 是一个具有两个 @Published 属性的 ObservableObject。WeatherView 声明 @ObservedObject var weather: WeatherService,从父级接收实例。当 temperature 更改时,objectWillChange 在新值设置之前触发,SwiftUI 重新绘制 WeatherView,并显示当前温度。订阅由 SwiftUI 自动管理 — 开发者无需调用 sink 或 dispose。

@ObservedObject 的使用模式

第一种模式 — 通过初始化器传递模型。父级通过 @StateObject 创建 ObservableObject 并将其作为 @ObservedObject 传递给子视图。这是一种标准的分层数据传递,根视图管理模型的生命周期,所有嵌套组件订阅更改。

第二种模式 — EnvironmentObject,通过 SwiftUI Environment 实现的 @ObservedObject 全局版本。对象在场景或根视图级别注入,并自动可供所有子组件使用,无需通过初始化器显式传递。在子视图内部,@EnvironmentObject 的工作方式类似于 @ObservedObject,但从环境中获取对象。

第三种模式 — 组合多个 ObservableObject。在复杂应用程序中,视图可以观察多个对象:@ObservedObject var user: UserService,@ObservedObject var network: NetworkMonitor。这分离了服务之间的责任并保持每个组件的可测试性。

swift
struct DashboardView: View {
    @ObservedObject var user: UserViewModel
    @ObservedObject var network: NetworkMonitor

    var body: some View {
        VStack {
            Text("欢迎, \\(user.name)")
            HStack {
                Circle()
                    .fill(network.isConnected ? Color.green : Color.red)
                    .frame(width: 10, height: 10)
            }
        }
    }
}

DashboardView 观察 UserViewModel 和 NetworkMonitor。每个对象负责自己的数据域,并独立地通知视图有关更改。如果网络断开,NetworkMonitor 更改 isConnected,SwiftUI 重新绘制 DashboardView,更新指示器颜色。ObservableObject 组合是 SwiftUI 应用程序中组织数据的首选方式。

@Published:ObservableObject 和 SwiftUI 的关联

@Published 是来自 Combine 的一个属性包装器,它自动为 ObservableObject 内部的属性添加一个发布器。当 @Published 属性更改时,Combine 通过 objectWillChange 发布器生成事件。SwiftUI 在使用 @ObservedObject 或 @StateObject 时订阅此发布器,并在每次新值时重新绘制视图。

@Published 支持所有类型,包括可选类型、集合和自定义结构。但是,对于集合(数组、字典),SwiftUI 只跟踪引用的替换,而不跟踪内容的更改。要检测元素的添加或删除,需要重新分配整个集合,或者使用手动调用 objectWillChange.send() 的 ObservableObject。

重要细节:@Published 必须只位于实现 ObservableObject 的类内部。在 ObservableObject 之外使用 @Published 会导致编译错误。此外,@Published 不能应用于惰性初始化属性(lazy var)或计算属性(computed property)。

使用 @ObservedObject 的常见错误

最关键的错误 — 使用 @ObservedObject 创建对象。如果您在父视图中编写 @ObservedObject var model = UserViewModel(),每次渲染都会创建一个新的 UserViewModel 实例。数据将丢失,@Published 订阅将被重新创建。始终使用 @StateObject 进行创建,仅使用 @ObservedObject 接收现成的对象。

第二个错误 — 在主线程之外修改 @Published 属性。ObservableObject 使用 Combine,它需要在主线程(main actor)上发送更改。如果您在后台队列中修改 @Published,SwiftUI 可能会在不合适的时候重新绘制视图,导致竞态条件。使用 DispatchQueue.main.async 或 @MainActor 进行更新。

第三个问题 — 循环更新。如果 @Published 的更改包含再次修改 @Published 的副作用,就会产生无限重绘循环。解决方案:使用保护标志(isUpdating)或将逻辑拆分到具有明确责任边界的不同 ObservableObject 中。

常见问题

@ObservedObject 可以是可选类型吗?

可以,SwiftUI 支持 @ObservedObject var model: UserViewModel?。但是,视图在对象为 nil 时不会订阅更改。当赋值时,订阅会自动激活。

@ObservedObject 和 @EnvironmentObject 有什么不同?

@ObservedObject 通过初始化器接收对象,@EnvironmentObject 通过 SwiftUI Environment。@EnvironmentObject 不需要通过构造函数显式传递,但对象必须在层次结构的顶层注入。

如何手动通知 SwiftUI 关于 ObservableObject 的变化?

在属性更改之前调用 objectWillChange.send()。如果 @Published 不适用(例如,对于计算属性或需要更改前报告变化的集合操作),这很有用。

为什么 @ObservedObject 在数组内部更改时不重新绘制视图?

@ObservedObject 和 @Published 跟踪引用的替换,而不是集合内容的修改。要重新绘制,需要重新分配数组:items.append(newItem) → items = items 或在修改之前使用 objectWillChange.send()。

@ObservedObject 可以在未实现 View 的结构中使用吗?

不可以,@ObservedObject 是一个仅在实现 View 协议的类型中可用的 SwiftUI 属性包装器。对于普通结构,请直接使用 Combine 和 ObservableObjectPublisher。

总结

  • @ObservedObject — 用于观察 ObservableObject 而不拥有它的属性包装器
  • @StateObject — 创建对象,@ObservedObject — 观察现有对象
  • @Published — ObservableObject 属性的自动发布器
  • 订阅 — SwiftUI 在使用 @ObservedObject 时自动管理 Combine 订阅
  • 组合 — 视图可以同时观察多个 ObservableObject
  • Main actor — @Published 属性应仅在主线程上修改
  • EnvironmentObject — @ObservedObject 的替代方案,用于通过环境隐式传递

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

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

讨论项目

另请阅读