深入理解 SwiftUI 中的 `@Observable` 与 `@Bindable`:从原理到实践

引言:为什么需要 @Observable

在 SwiftUI 的早期版本中,我们使用 @StateObject@ObservedObject 来管理状态,但这些方式依赖于 ObservableObject 协议,需要手动标记 @Published 属性,并且存在性能瓶颈和样板代码。

Apple 在 iOS 17 引入了全新的 Observation 框架,通过 @Observable 宏(macro)简化了状态管理,无需继承 ObservableObject,也无需手动标记 @Published,性能更优,代码更清晰。

核心概念解析

@Observable 是什么?

@Observable 是一个宏(macro),用于修饰类(class),使其属性具备可观察性。

@Observable 修饰的类,其所有存储属性(stored properties)默认都是可观察的,无需 @Published

swift 复制代码
import Observation

@Observable
class Counter {
    var value: Int = 0
    var name: String = "Counter"
}

✅ 注意:@Observable 只能用于 class,不能用于 struct。

@Bindable 是什么?

@Bindable 是一个属性包装器(property wrapper),用于在视图中创建对 @Observable 类属性的绑定(Binding)。

它通常用于子视图中,将父视图传入的可观察对象中的某个属性绑定到 UI 控件(如 TextFieldToggle 等)。

swift 复制代码
struct CounterView: View {
    @Bindable var counter: Counter

    var body: some View {
        VStack {
            Text("Current value: \(counter.value)")
            Stepper("Increment", value: $counter.value) // ✅ 使用 $ 创建绑定
            TextField("Name", text: $counter.name)
        }
    }
}

✅ 关键点:@Bindable 让你可以在子视图中对 @Observable 对象的属性进行 双向绑定。

完整代码示例:从模型到视图

定义数据模型(Model)

swift 复制代码
import Foundation
import Observation

@Observable
class Counter {
    var value: Int = 0
    var name: String = "My Counter"
}

主视图(ContentView)

swift 复制代码
import SwiftUI

struct ContentView: View {
    // ✅ 使用 State 持有可观察对象
    @State private var counter = Counter()

    var body: some View {
        NavigationStack {
            VStack(spacing: 20) {
                Text("Counter Name: \(counter.name)")
                    .font(.headline)

                Text("Value: \(counter.value)")
                    .font(.largeTitle)

                // ✅ 传入子视图,使用 Bindable 进行绑定
                CounterView(counter: counter)

                Button("Reset") {
                    counter.value = 0
                    counter.name = "Reset Counter"
                }
            }
            .padding()
            .navigationTitle("Observation Demo")
        }
    }
}

子视图(CounterView)使用 @Bindable

swift 复制代码
import SwiftUI

struct CounterView: View {
    @Bindable var counter: Counter // ✅ 使用 Bindable 包装,支持绑定

    var body: some View {
        VStack(spacing: 15) {
            Stepper("Increment", value: \(counter.value)) // ✅ 绑定 value
                .padding()

            TextField("Enter counter name", text: \(counter.name)) // ✅ 绑定 name
                .textFieldStyle(.roundedBorder)
                .padding(.horizontal)
        }
    }
}

预览(Preview)

swift 复制代码
#Preview {
    ContentView()
}

运行效果与行为说明

操作 结果
点击 Stepper counter.value改变,主视图自动更新
修改 TextField counter.name改变,主视图标题同步更新
点击 Reset 按钮 重置 valuename,视图刷新

✅ 所有更新都是响应式的,无需手动调用 objectWillChange.send()

常见误区与注意事项

错误用法 正确做法
在 struct 上使用 @Observable ❌ 只能用于 class
@ObservedObject接收 @Observable对象 ❌ 应使用 @State@Bindable
忘记在子视图中用 @Bindable ❌ 无法创建绑定,编译器报错:_counter.valueis not a Binding
使用 @Published ❌ 不需要,所有属性默认可观察

总结与见解

✅ 优点

  1. 简化代码:无需继承 ObservableObject,无需 @Published
  2. 性能更好:Observation 框架使用更高效的依赖追踪机制
  3. 类型安全:宏展开在编译期完成,减少运行时错误
  4. 支持绑定:@Bindable 让子视图轻松实现双向绑定

⚠️ 限制与思考

  • 仅支持 iOS 17+ / macOS 14+ / Xcode 15+
  • 只能用于 class,不支持 struct(但 struct 本身值语义,可用 @State
  • 不支持 嵌套观察:如果属性是另一个自定义类型,需单独标记为 @Observable

扩展应用场景

  1. 表单编辑(Form Editing)
swift 复制代码
@Observable
class UserProfile {
    var name: String = ""
    var email: String = ""
    var isSubscribed: Bool = false
}

// 在视图中使用 Form + Section + TextField + Toggle
  1. 多视图共享状态(Shared State)
swift 复制代码
@main
struct MyApp: App {
    @State private var user = UserProfile()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(user) // ✅ 使用 environment 注入
        }
    }
}

// 子视图中使用:
@Environment(UserProfile.self) private var user
  1. @Query 结合(SwiftData)
swift 复制代码
@Query var items: [Item]
@State private var selectedItem: Item?

未来趋势:@Observable 将成为 SwiftUI 状态管理的主流方式,逐步替代 ObservableObject

结语:拥抱 Observation,告别样板代码

SwiftUI 的 @Observable@Bindable 标志着 Apple 在状态管理上的一次重大飞跃。它不仅让代码更简洁、响应更快,还降低了学习门槛。对于新项目,强烈推荐使用 Observation 框架;对于旧项目,也可逐步迁移,享受现代化开发的便利。

🚀 未来已来,是时候告别 @PublishedObservableObject 了!

参考资料

相关推荐
00后程序员张1 天前
iOS 26 兼容测试实战,机型兼容、SwiftUI 兼容性改动
android·ios·小程序·uni-app·swiftui·cocoa·iphone
大熊猫侯佩2 天前
雪山飞狐之 Swift 6.2 并发秘典:@concurrent 的江湖往事
swiftui·swift·apple
胎粉仔4 天前
Objective-C —— APIs declaration 自定义
ios·objective-c·swift
用户094 天前
Swift Concurrency 中的 Threads 与 Tasks
ios·swiftui·swift
低调小一4 天前
双端 FPS 全景解析:Android 与 iOS 的渲染机制、监控与优化
android·ios·kotlin·swift·fps
用户094 天前
更现代、更安全:Swift Synchronization 框架与 Mutex 锁
ios·面试·swift
大熊猫侯佩7 天前
鹿鼎记豪侠传:Rust 重塑 iOS 江湖(下)
rust·objective-c·swift
大熊猫侯佩7 天前
鹿鼎记豪侠传:Rust 重塑 iOS 江湖(上)
rust·objective-c·swift