SwiftUI 5中使用@Observable对象时是否无需@State属性包装器?
我在苹果开发者文档的「在视图中更改模型数据」章节看到:
借助SwiftUI的Observation,视图无需使用属性包装器或绑定即可支持数据变更
于是编写了如下测试代码:
struct ContentView: View { var person: Person = .init(id: UUID(), name: "Stacey") var body: some View { VStack { Button { person.name = "Sarah" } label: { Text("change name") } AnotherView() .environment(person) Text("Name: \(person.name)") } } } @Observable class Person: Identifiable { var id: UUID var name: String init(id: UUID, name: String) { self.id = id self.name = name } }
代码能正常编译运行,但我有疑问:既然用@Observable对象时无需@State也能读写变量,那@State还有什么意义?
解答
1. 避免视图重建导致的数据丢失
@Observable是作用在引用类型上的观察机制,但直接用var person声明的对象,是存储在视图结构体内部的。当SwiftUI因为父视图刷新、布局变化等原因重建ContentView结构体时,会重新初始化一个新的Person实例,之前修改的name数据会直接丢失。
而@State会把对象存储在SwiftUI专门的视图状态容器中,和视图结构体的生命周期解耦,无论视图重建多少次,都能保留原来的Person实例和数据。
2. 触发视图UI更新
你的示例中点击按钮修改person.name后,界面上的Text("Name: \(person.name)")并不会更新——因为var person是视图结构体的普通属性,SwiftUI不会监听它引用的@Observable对象的变化。虽然你能修改对象的属性,但视图本身不会触发body的重新计算。
如果把var person改成@State private var person = Person(...),SwiftUI会自动跟踪这个@Observable对象的变化,当对象的属性更新时,会立刻触发视图刷新,UI才会同步显示最新的name值。
3. 明确视图的私有状态边界
@State的核心作用是标记视图的私有内部状态,适合存储仅当前视图需要管理的数据。而@Observable对象更多用于跨视图共享的模型数据(比如通过environment传递给子视图)。如果某个@Observable对象是当前视图独有的,用@State持有它,既能保证数据持久,又能让视图响应其变化,这是普通属性做不到的。
内容的提问来源于stack exchange,提问作者JAHelia

