SwiftUI ObservableObject 观察者模式学习笔记
什么是 ObservableObject
ObservableObject 是 SwiftUI 中实现观察者模式的协议,允许多个视图观察同一个数据源,当数据发生变化时,所有相关视图会自动更新UI。
核心概念
观察者模式的本质
- 一个数据源,多个观察者
- 数据驱动UI更新:数据变化 → 自动刷新视图
- 状态集中管理:统一的数据管理中心
数据流方向
swift
数据源 (ObservableObject)
↓ 数据变化
多个视图 (观察者) 自动更新
核心组件
1. @Published 属性包装器
swift
class DataStore: ObservableObject {
@Published var count: Int = 0 // 可观察属性
@Published var name: String = "" // 当值改变时自动通知观察者
}
作用:标记需要观察的属性,值改变时自动通知所有观察者
2. @StateObject 属性包装器
swift
struct ContentView: View {
@StateObject private var store = DataStore() // 创建并管理实例
}
作用:创建并拥有 ObservableObject 实例的生命周期
3. @ObservedObject 属性包装器
swift
struct ChildView: View {
@ObservedObject var store: DataStore // 观察传入的实例
}
作用:观察已存在的 ObservableObject 实例
实现步骤
步骤1:创建数据源类
swift
class Counter: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1 // 修改 @Published 属性会自动通知观察者
}
}
步骤2:主视图创建实例
swift
struct ContentView: View {
@StateObject private var counter = Counter()
var body: some View {
// 将实例传递给子视图
ChildView(counter: counter)
}
}
步骤3:子视图观察数据
swift
struct ChildView: View {
@ObservedObject var counter: Counter
var body: some View {
Text("Count: (counter.count)") // 自动响应数据变化
}
}
@StateObject vs @ObservedObject
特性 | @StateObject | @ObservedObject |
---|---|---|
职责 | 创建并管理实例 | 观察已有实例 |
生命周期 | 与视图绑定 | 由外部管理 |
使用场景 | 根视图或数据拥有者 | 子视图或数据使用者 |
内存管理 | 自动处理 | 依赖传入方 |
swift
// ✅ 正确用法
struct ParentView: View {
@StateObject private var store = DataStore() // 创建者用 @StateObject
var body: some View {
ChildView(store: store)
}
}
struct ChildView: View {
@ObservedObject var store: DataStore // 使用者用 @ObservedObject
}
工作流程
swift
1. 用户操作 (点击按钮等)
↓
2. 调用业务方法 (store.increment())
↓
3. 修改 @Published 属性 (count += 1)
↓
4. 自动通知所有观察者
↓
5. 观察者视图重新渲染
↓
6. UI 自动更新显示新数据
应用场景
1. 计数器应用
swift
class Counter: ObservableObject {
@Published var count = 0
func increment() { count += 1 }
}
2. 用户信息管理
swift
class UserStore: ObservableObject {
@Published var currentUser: User?
@Published var isLoggedIn = false
}
3. 购物车状态
swift
class ShoppingCart: ObservableObject {
@Published var items: [Item] = []
@Published var totalPrice: Double = 0
}
4. 网络请求状态
swift
class NetworkManager: ObservableObject {
@Published var isLoading = false
@Published var data: [String] = []
@Published var errorMessage: String?
}
最佳实践
1. 命名规范
swift
// 推荐的命名模式
class UserStore: ObservableObject { } // Store 后缀
class DataManager: ObservableObject { } // Manager 后缀
class AppState: ObservableObject { } // State 后缀
2. 属性分类
swift
class AppStore: ObservableObject {
// 可观察属性
@Published var userName: String = ""
@Published var isLoading: Bool = false
// 私有属性(不需要观察)
private let apiKey: String = "secret"
// 计算属性
var displayName: String {
userName.isEmpty ? "Guest" : userName
}
}
3. 业务方法封装
swift
class TodoStore: ObservableObject {
@Published var todos: [Todo] = []
// 封装业务逻辑,不直接暴露数据修改
func addTodo(_ title: String) {
let newTodo = Todo(title: title)
todos.append(newTodo)
}
func completeTodo(_ id: UUID) {
if let index = todos.firstIndex(where: { $0.id == id }) {
todos[index].isCompleted = true
}
}
}
4. 避免过度使用
swift
// ❌ 不好:为简单数据创建 ObservableObject
class SimpleCounter: ObservableObject {
@Published var count = 0
}
// ✅ 更好:简单数据用 @State
struct CounterView: View {
@State private var count = 0 // 简单状态用 @State
}
常见错误
1. 混淆 @StateObject 和 @ObservedObject
swift
// ❌ 错误:子视图用 @StateObject 会创建新实例
struct ChildView: View {
@StateObject var store = DataStore() // 每次重新创建
}
// ✅ 正确:子视图用 @ObservedObject 观察传入的实例
struct ChildView: View {
@ObservedObject var store: DataStore
}
2. 忘记标记 @Published
swift
class DataStore: ObservableObject {
var count: Int = 0 // ❌ 忘记 @Published,不会触发更新
@Published var count: Int = 0 // ✅ 正确
}
3. 在非主线程修改 @Published 属性
swift
class DataStore: ObservableObject {
@Published var data: [String] = []
func loadData() {
DispatchQueue.global().async {
// ❌ 错误:后台线程修改 @Published 属性
self.data = ["new data"]
// ✅ 正确:切换到主线程
DispatchQueue.main.async {
self.data = ["new data"]
}
}
}
}
与其他模式对比
vs. @State/@Binding
- @State/@Binding:简单的视图内部状态
- ObservableObject:复杂的跨视图状态管理
vs. 回调模式
- 回调:一对一,事件驱动,临时通信
- ObservableObject:一对多,状态驱动,持续观察
vs. 环境对象 (@EnvironmentObject)
- @ObservedObject:显式传递,层级清晰
- @EnvironmentObject:环境注入,跨层级传递
性能优化
1. 避免不必要的更新
swift
class OptimizedStore: ObservableObject {
@Published var importantData: String = ""
// 不需要观察的数据不用 @Published
private var cacheData: [String] = []
// 批量更新避免频繁刷新
func batchUpdate() {
// 多个操作...
objectWillChange.send() // 手动触发一次更新
}
}
2. 合理分割数据源
swift
// ❌ 避免:巨大的单一数据源
class MassiveStore: ObservableObject {
@Published var userData: User?
@Published var posts: [Post] = []
@Published var comments: [Comment] = []
// ... 太多属性
}
// ✅ 推荐:按功能分割
class UserStore: ObservableObject {
@Published var currentUser: User?
}
class PostStore: ObservableObject {
@Published var posts: [Post] = []
}
总结
ObservableObject 是 SwiftUI 中强大的状态管理工具:
优势
✅ 自动更新 :数据变化时UI自动刷新
✅ 一对多 :一个数据源支持多个观察者
✅ 集中管理 :统一的状态管理中心
✅ 类型安全:编译时类型检查
适用场景
- 复杂的应用状态管理
- 多个视图共享数据
- 需要响应式更新的场景
- 业务逻辑与视图分离
记忆要点
- @Published = "我变了就通知大家"
- @StateObject = "我创建并管理你"
- @ObservedObject = "我在观察你"
掌握 ObservableObject 是构建复杂 SwiftUI 应用的关键技能,它让状态管理变得简单而强大。