SwiftUI随记:@StateObject 和 @State的区别

442 阅读3分钟

1. 简介

@StateObject@State 是 SwiftUI 中的两种属性包装器,用于管理视图中的状态。它们的功能和使用场景有所不同,以下是详细的区别和应用场景:

2.具体用法

  • @State
    • 适用场景

      • 用于管理简单值类型(如 IntStringBool 等)或非复杂的状态。
      • 状态由当前视图管理,并且只存在于当前视图的生命周期内。
    • 作用:SwiftUI 负责在视图生命周期中持有该变量并在状态改变时重新渲染视图。

    • 生命周期:状态的生命周期与当前视图一致。当视图被销毁时,@State 变量也会被释放。

    struct CounterView: View {
    @State private var count = 0 // 由当前视图管理的简单状态
    
    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increment") {
                count += 1 // 修改状态后,视图会自动刷新
            }
        }
    }
    

}

  • @StateObject
    • 适用场景
      • 用于管理复杂状态或需要遵循 ObservableObject 协议的引用类型(如类)。
      • 通常用于视图初始化时创建对象,并需要由 SwiftUI 管理该对象的生命周期。
    • 作用:确保在视图重新加载时不会重新创建对象
    • 生命周期@StateObject 的生命周期由 SwiftUI 管理,与它所属的视图一致。当视图被销毁时,@StateObject 也会被释放。
class CounterModel: ObservableObject {
    @Published var count = 0
}

struct CounterView: View {
    @StateObject private var counterModel = CounterModel() // 管理引用类型的状态

    var body: some View {
        VStack {
            Text("Count: \(counterModel.count)")
            Button("Increment") {
                counterModel.count += 1 // 视图会在 count 改变时刷新
            }
        }
    }
}
特性@State@StateObject
适用类型值类型(如 IntString引用类型(遵循 ObservableObject 的类)
作用范围仅限当前视图当前视图及其子视图
生命周期与当前视图绑定与当前视图绑定
是否可观察属性变化不需要 @Published需要 @Published 标记属性
对象重建每次视图重建都会重新创建SwiftUI 确保对象在视图生命周期中唯一

3. 使用场景对比

简单值管理:使用 @State

如果状态是一个简单值类型,并且只在当前视图中使用,建议使用 @State

swift
复制代码
struct ToggleView: View {
    @State private var isOn = false

    var body: some View {
        Toggle("Enable", isOn: $isOn)
    }
}
复杂状态管理:使用 @StateObject

如果状态需要在多个子视图中共享,并且是引用类型,建议使用 @StateObject

swift
复制代码
class UserSettings: ObservableObject {
    @Published var username = "Guest"
}

struct ParentView: View {
    @StateObject private var settings = UserSettings()

    var body: some View {
        ChildView()
            .environmentObject(settings) // 在子视图中共享状态
    }
}

struct ChildView: View {
    @EnvironmentObject var settings: UserSettings

    var body: some View {
        Text("Username: (settings.username)")
    }
}

配合使用的场景:@ObservedObject@EnvironmentObject

  1. @ObservedObject

    • 用于父视图传递 ObservableObject 状态到子视图。
    • 不负责创建或管理对象。
  2. @EnvironmentObject

    • 适用于跨层级共享状态,子视图无需显式接收。
    • 父视图负责注入环境对象。
swift
复制代码
class AppState: ObservableObject {
    @Published var isLoggedIn = false
}

struct ParentView: View {
    @StateObject var appState = AppState()

    var body: some View {
        ChildView()
            .environmentObject(appState)
    }
}

struct ChildView: View {
    @EnvironmentObject var appState: AppState

    var body: some View {
        Text(appState.isLoggedIn ? "Welcome!" : "Please Log In")
    }
}

总结

  • @State:适合简单值类型,状态只需要在当前视图中管理。

  • @StateObject:适合复杂的引用类型状态,且需要与子视图共享时使用。

  • 最佳实践

    • 使用 @State 管理局部、简单状态。
    • 使用 @StateObject 创建和持有复杂的引用类型状态。
    • 配合 @EnvironmentObject 在视图层级中注入和共享状态。