SwiftUI中@State、@Binding、@Bindable的差异及选型建议
SwiftUI中@State、@Binding、@Bindable的差异与选择
首先我们定义了一个基于@Observable的引用类型数据类:
@Observable class TextClass { var text: String = "" }
下面针对父视图与子视图的双向绑定场景,分析三种属性包装器的核心差异及适用场景:
一、三种实现的核心差异
1. 子视图使用@State(误用场景)
// 父视图 struct ContentView: View { @State var text: TextClass = .init() var body: some View { Form { Text(text.text) Button("upup") { text.text += "1" } } Form { ChildView(text: text) } } } // 子视图(误用@State) struct ChildView: View { @State var text: TextClass var body: some View { Text(text.text) Button("upup Child") { text.text += "2" } } }
- 语义违背:@State的设计目的是存储视图自身私有的内部状态,无论是值类型还是引用类型,都应该由当前视图初始化并独占管理。子视图通过@State接收外部传入的实例,完全不符合@State的语义。
- 潜在问题:虽然因为
TextClass是引用类型,修改属性时会同步到父视图,但如果父视图因自身状态变化重建,子视图的@State可能保留旧引用,引发状态不一致问题。 - 结论:这是规范外的误用,绝不推荐。
2. 使用@Binding
// 父视图 struct ContentView: View { @State var text: TextClass = .init() var body: some View { Form { Text(text.text) Button("upup") { text.text += "1" } } Form { ChildView(text: $text) } } } // 子视图 struct ChildView: View { @Binding var text: TextClass var body: some View { Text(text.text) Button("upup Child") { text.text += "2" } } }
- 语义定位:@Binding是对外部状态的双向引用,本身不存储状态,仅负责传递对现有状态的读写权限。
- 行为逻辑:子视图通过
@Binding接收父视图@State变量的绑定,修改text.text时直接操作父视图持有的实例,Observable的属性变化会触发父子视图更新。 - 局限性:需要用
$符号传递绑定,更适合值类型的双向绑定场景;对于Observable引用类型,它只是传递对象的绑定,并非专门针对Observable的观察优化。
3. 使用@Bindable
// 父视图 struct ContentView: View { @State var text: TextClass = .init() var body: some View { Form { Text(text.text) Button("upup") { text.text += "1" } } Form { ChildView(text: text) } } } // 子视图 struct ChildView: View { @Bindable var text: TextClass var body: some View { Text(text.text) Button("upup Child") { text.text += "2" } } }
- 语义定位:@Bindable是SwiftUI为
@Observable对象专门设计的包装器,用于让视图订阅对象的属性变化,并支持修改属性。 - 行为逻辑:子视图直接接收父视图的
TextClass实例,@Bindable会自动为视图绑定对象的属性监听,修改属性时父子视图都会响应更新。 - 优势:语义清晰,无需传递
$绑定符号,代码更简洁,完全适配iOS 17+之后的Observable状态管理模式。
二、实际开发中的选择建议
- 彻底避免子视图用@State接收外部状态:这是不符合SwiftUI设计规范的误用,会导致代码维护难度提升,状态逻辑混乱。
- iOS 16及以下版本:只能选择@Binding,因为@Bindable是iOS 17才引入的API。
- iOS 17+/macOS 14+及以上版本:优先选择@Bindable——它是专门为Observable对象打造的方案,语义更贴合新的状态管理模式,代码更简洁易读。
- @Binding的适用场景:更多用于值类型的双向绑定(如
String、Bool等),或在需要传递整个对象的绑定权限时使用,但对于Observable引用类型,@Bindable是更优解。
内容的提问来源于stack exchange,提问作者Deok
相关产品推荐
相关产品推荐

