6-9.【架构设计】@State、@StateObject、@ObservedObject、@EnvironmentObject 如何说清楚它们的所有权模型?

15 阅读3分钟

一、四句话(所有权模型一句话版)

  • @State
    👉 “这个值的所有权在 View 自己,生命周期由 SwiftUI 管理。”
  • @StateObject
    👉 “这个引用类型对象由当前 View 创建并拥有,只创建一次。”
  • @ObservedObject
    👉 “这个对象不是我创建的,我只是临时观察它。”
  • @EnvironmentObject
    👉 “这个对象由更高层统一拥有,我只是从环境里拿来用。”

如果你只记住这四句,80% 场景已经不会踩雷。


二、把“所有权”翻译成三个问题(最关键)

每次选属性包装器,只问自己这三件事:

  1. 是谁创建的?
  2. 谁负责活多久?
  3. View 消失时,它要不要跟着消失?

三、完整对照表(所有权 + 生命周期)

包装器谁创建谁拥有生命周期绑定典型用途
@StateViewSwiftUI State StoreView identity简单值类型状态
@StateObjectViewViewView identity(一次)ViewModel / Store
@ObservedObject外部外部外部决定子 View 观察
@EnvironmentObject根容器全局环境App / Scene全局共享状态

四、最容易混淆的两个(重点)

1️⃣ @StateObject vs @ObservedObject

一句话区别:

init 的,谁就用 @StateObject
init 的,就用 @ObservedObject

// 正确
struct ParentView: View {
    @StateObject var vm = UserViewModel()
}

// 正确
struct ChildView: View {
    @ObservedObject var vm: UserViewModel
}

❌ 错误用法:

@ObservedObject var vm = UserViewModel() // 会被反复创建

2️⃣ @EnvironmentObject 的本质

不是“魔法全局变量”,而是:

由更高层 View 显式注入的依赖

@main
struct App: App {
    let session = SessionStore()

    var body: some Scene {
        WindowGroup {
            RootView()
                .environmentObject(session)
        }
    }
}
  • View 不负责创建
  • View 假定“环境中一定存在”
  • 缺失会直接 crash(设计使然)

五、一个超级好记的心智模型(推荐)

把它们当成 资源管理声明

@State            = 我有一个小本子
@StateObject      = 我养了一只宠物
@ObservedObject   = 我只是帮别人看宠物
@EnvironmentObject= 社区公共设施

六、错误使用的典型后果(速查)

错误后果
用 @ObservedObject 创建对象View 重建 → 对象重建
用 @StateObject 接收外部对象所有权混乱
滥用 @EnvironmentObject隐式依赖难以维护
用 @State 存引用类型生命周期不可控

最终一句话(压轴版)

SwiftUI 的属性包装器不是“状态类型”,而是“所有权声明”:你在告诉系统——这个状态是谁创建的、谁负责活多久。

英文版

6-9. [Architecture Design] @State, @StateObject, @ObservedObject, @EnvironmentObject: How to Explain Their Ownership Models?

I. Four Sentences (The One-Sentence Version of the Ownership Model)
  • @State 👉 "The ownership of this value belongs to the View itself; its lifecycle is managed by the SwiftUI State Store."
  • @StateObject 👉 "This reference type object is created and owned by the current View; it is initialized only once."
  • @ObservedObject 👉 "I did not create this object; I am just observing it temporarily."
  • @EnvironmentObject 👉 "This object is owned collectively at a higher level; I am just pulling it from the environment to use it."

If you remember these four sentences, you will avoid 80% of common pitfalls.


II. Translating "Ownership" into Three Key Questions

Whenever choosing a property wrapper, ask yourself these three things:

  1. Who created it?
  2. Who is responsible for how long it lives?
  3. When the View disappears, should this object disappear with it?

III. Complete Comparison Table (Ownership + Lifecycle)
WrapperCreatorOwnerLifecycle BindingTypical Use Case
@StateViewSwiftUI State StoreView IdentitySimple value-type states
@StateObjectViewViewView Identity (created once)ViewModel / Store
@ObservedObjectExternalExternalDetermined by ExternalSub-view observation
@EnvironmentObjectRoot ContainerGlobal EnvironmentApp / SceneGlobal shared state

IV. The Two Most Easily Confused Wrappers

1. @StateObject vs. @ObservedObject

The core distinction:

  • If you initialize (init) the object inside the view, use @StateObject.
  • If the object is passed in (not initialized by the view), use @ObservedObject.

Swift

// CORRECT
struct ParentView: View {
    @StateObject var vm = UserViewModel() // Parent owns it
}

// CORRECT
struct ChildView: View {
    @ObservedObject var vm: UserViewModel // Child just observes it
}

❌ Wrong Usage:

@ObservedObject var vm = UserViewModel() // The object will be repeatedly re-created whenever the view updates.

2. The Essence of @EnvironmentObject

It is not a "magical global variable," but rather:

  • A dependency explicitly injected by a higher-level View.
  • The View is not responsible for creation.
  • The View assumes "it must exist in the environment."
  • If missing, it will cause a crash (by design).

V. A Super Memorable Mental Model

Treat them as declarations of resource management:

  • @State = I have a small notebook.
  • @StateObject = I own a pet.
  • @ObservedObject = I am just pet-sitting for someone else.
  • @EnvironmentObject = Community public facilities.

VI. Typical Consequences of Incorrect Use (Quick Check)
ErrorConsequence
Using @ObservedObject to create an objectView rebuild → Object is re-created (State lost)
Using @StateObject to receive an external objectOwnership confusion / unexpected persistence
Abusing @EnvironmentObjectImplicit dependencies become hard to maintain
Using @State for reference typesUncontrollable lifecycle

Final Summary

SwiftUI property wrappers are not just "state types"; they are "Ownership Declarations." You are telling the system: who created this state, and who is responsible for its lifespan.