Swift @StateObject和直接实例化对比分析
Swift @StateObject和直接实例化对比分析

Swift @StateObject和直接实例化对比分析

前文提到了《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 或其他状态管理方式来避免潜在问题。

相关资料

  1. 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/
  2. 《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/
  3. 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/

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注