SwiftUI ObservableObject 观察者模式学习笔记

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 应用的关键技能,它让状态管理变得简单而强大。

相关推荐
跟橙姐学代码5 小时前
列表、元组与字典:Python开发者的三大必备利器,再向高手靠近一步
前端·python·ipython
蜗牛快跑1235 小时前
拆巨资让 Claude Code 和 Codex 同时住进了我的终端里
前端·后端·ai编程
小高0075 小时前
🔥🔥🔥Vue部署踩坑全记录:publicPath和base到底啥区别?99%的前端都搞错过!
前端·vue.js·面试
摸着石头过河的石头5 小时前
HTTP内容类型:从基础到实战的全方位解析
前端·http·浏览器
luckyzlb5 小时前
02- html && css
前端·css·html
AI@独行侠5 小时前
04 - 【HTML】- 常用标签(下篇)
前端·html
空山新雨(大队长)5 小时前
HTML第九课:HTML5新增标签
前端·html·html5
Wish3D5 小时前
在前端开发中,html中script 的type分别有哪几种?分别什么情况用到?
前端·html