深入理解 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 了!

参考资料

相关推荐
非专业程序员Ping13 小时前
一文读懂字符、字形、字体
ios·swift·font
东坡肘子18 小时前
去 Apple Store 修手机 | 肘子的 Swift 周报 #0107
swiftui·swift·apple
非专业程序员2 天前
iOS/Swift:深入理解iOS CoreText API
ios·swift
xingxing_F2 天前
Swift Publisher for Mac 版面设计和编辑工具
开发语言·macos·swift
YGGP3 天前
【Swift】LeetCode 438. 找到字符串中所有字母异位词
swift
QWQ___qwq3 天前
Swift中.gesture的用法
服务器·microsoft·swift
QWQ___qwq3 天前
SwiftUI 布局之美:Padding 让界面呼吸感拉满
ios·swiftui·swift
用户094 天前
SwiftUI 键盘快捷键作用域深度解析
ios·面试·swiftui
用户094 天前
Xcode 26 的10个新特性解析
ios·面试·swift
白熊1884 天前
【图像大模型】ms-swift 深度解析:一站式多模态大模型微调与部署框架的全流程使用指南
开发语言·ios·swift