前文提到了《Swift中常用的四种属性包装器》,现在将拿出代码分析@StateObjcet的生命后期管理和持久性。
场景1
@main
struct MyApp: App {
@StateObject private var counter = Counter() // 正确的写法
var body: some Scene {
WindowGroup {
ParentView().environmentObject(counter)
}
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ParentView().environmentObject(Counter())
}
}
}
这是两种属性包装器的用法,第一种使用了@StateObject创建对应的对象,并且通过@EnvironmentObject注入到ParentView()视图中。第二种用法是通过@EnvironmentObject将Counter()对象通过直接注入到ParentView()视图中。
使用@StateObject
@StateObject 管理生命周期:counter 是由 @StateObject 管理的,这意味着 SwiftUI 确保它在 MyApp 的生命周期内只会被创建一次,并且不会在视图更新时被重新创建。
持久性:counter 的状态(例如 Counter 中的 count 属性)会在整个应用运行期间保持不变,直到应用被关闭。这种方式确保 counter 的状态在不同视图之间共享,并且不会被意外重置。
适用于状态需要长期存在的情况:例如计数器、用户登录信息、全局配置等。
直接使用 Counter()
没有状态管理:每次 body 重新渲染时,Counter() 会被重新实例化。这意味着 ParentView 和其他使用 Counter 的子视图会获得一个新的 Counter 对象,所有状态会重置。
状态不持久:这可能导致预期外的行为,因为每次视图更新时,counter 会重置。
适用于状态短暂、无需持久的情况:例如只想在某个特定视图期间维持状态的对象。
场景2
// 使用@StateObject
struct ParentView: View {
@StateObject private var counter = Counter() // 只实例化一次
var body: some View {
ChildView(counter: counter)
}
}
// 直接实例化
struct ParentView: View {
var body: some View {
ChildView(counter: Counter()) // 将对象传递给子视图
}
}
// 子视图
struct ChildView: View {
@ObservedObject var counter: Counter // 引用父视图传递的对象
var body: some View {
VStack {
Text("Count: \(counter.count)")
Button("Increment") {
counter.count += 1
}
}
}
}
场景2和场景1的区别在于,没有使用@EnvironmentObject在顶部视图注入。
使用@StateObject
@StateObject 是用于管理 ObservableObject 生命周期的。如果 ParentView 拥有 Counter,那么 ParentView 的生命周期会决定 Counter 的生命周期。当 ParentView 被创建时,Counter 也会被创建,并且在 ParentView 销毁之前,Counter 会保持不变。使用 @StateObject 确保了 Counter 只会被实例化一次。
直接实例化 Counter() 的问题
在 ParentView 中直接创建一个新的 Counter() 并传递给 ChildView:
struct ParentView: View {
var body: some View {
ChildView(counter: Counter()) // 每次都会创建新的 Counter 实例
}
}
发生了什么?
每次 ParentView 的 body 重新渲染时,都会创建一个 新的 Counter 实例。这意味着:
1、状态不会持久:如果 ParentView 被重新渲染(例如某个外部状态变化),Counter 会被重新实例化,count 的值会重置。
2、生命周期不受控:SwiftUI 没有机制跟踪和管理这个 Counter 的实例,这会导致预期之外的行为。
总结
需要持久化和长期状态管理时:选择第一种,使用 @StateObject 来管理对象生命周期。这确保状态在整个应用生命周期内保持不变。
对象每次视图重绘都能重置或短期存在时:可以选择第二种,但一般来说,更推荐用 @StateObject 或其他状态管理方式来避免潜在问题。
相关资料
- Swift通过 @EnvironmentObjec共享和传递数据:https://fangjunyu.com/2024/10/23/swift%e9%80%9a%e8%bf%87-environmentobjec%e5%85%b1%e4%ba%ab%e5%92%8c%e4%bc%a0%e9%80%92%e6%95%b0%e6%8d%ae/
- 《Swift 从实战到理论:@publish 和@ObservedObject》:https://fangjunyu.com/2024/10/22/swift-%e4%bb%8e%e5%ae%9e%e6%88%98%e5%88%b0%e7%90%86%e8%ae%ba%ef%bc%9apublish-%e5%92%8c-observedobject/
- Swift中常用的四种属性包装器:https://fangjunyu.com/2024/10/23/swift%e4%b8%ad%e5%b8%b8%e7%94%a8%e7%9a%84%e5%9b%9b%e7%a7%8d%e5%b1%9e%e6%80%a7%e5%8c%85%e8%a3%85%e5%99%a8/