一句话结论(先给直觉)
SwiftUI 的
View不是 UI 本身,而是“如何构建 UI 的说明书(blueprint)”;
真正“持续存在”的,是 SwiftUI 在幕后维护的一棵 UI 状态树(渲染树)。
所以:
View是 值类型、随时可以被丢弃- UI 的持续性来自 State + Identity + Diff 算法,而不是 View 实例
一、先把最大的误解拆掉
❌ 错误直觉(UIKit 思维)
View struct = 屏幕上的那个 UI
✅ SwiftUI 的真实模型
View struct = 描述 UI 的函数 / 配方
UI 实体 = SwiftUI Runtime 管理的 Render Tree
你写的:
struct ContentView: View {
var body: some View {
Text("Hello")
}
}
并不是在“创建一个 Text 对象”,而是在说:
“当前状态下,UI 应该长这样。”
二、SwiftUI 真正“持续存在”的是什么?
不是 View,而是 三样东西:
1️⃣ State(状态)
@State var count: Int
@StateObject var viewModel
- State 不存放在 View struct 里
- SwiftUI Runtime 把它们存到一个 独立的状态存储(State Store)
即使 View struct 被销毁、重建:
View struct ❌ 重建
State Store ✅ 继续存在
2️⃣ Identity(身份)
SwiftUI 通过 identity 知道:
“这是同一个 UI 元素,还是一个新的?”
来源包括:
- View 在树中的位置
.id(...)ForEach的 id@StateObject的创建点
这解决了一个关键问题:
“我这次生成的 View,是不是上一次那个?”
3️⃣ Render Tree(渲染树)
SwiftUI 在幕后维护一棵 引用类型的 UI 树:
RenderNode (Text)
RenderNode (Button)
RenderNode (VStack)
- 类似 UIKit 的
UIView树 - 但你 永远不直接接触
- SwiftUI 负责 diff & 更新
三、状态变化时发生了什么?(关键流程)
假设你有:
struct CounterView: View {
@State var count = 0
var body: some View {
Text("(count)")
Button("+") { count += 1 }
}
}
当你点按钮:
① State 改变
State Store: count = 0 → 1
② SwiftUI 重新“调用 body”
重新生成 CounterView(新的 struct 值)
⚠️ 注意:
不是“更新 View”,而是“重新计算 View 描述”
③ Diff(差分)
SwiftUI 比较:
上一次 View 描述
vs
这一次 View 描述
发现:
- Text 的内容变了
- Button 没变
④ 最小化更新 Render Tree
只更新 Text 对应的 RenderNode
👉 UI 看起来“持续存在”,但其实是 精准替换
四、为什么用值类型反而是优势?
1️⃣ View = 纯函数
View = f(State)
- 没有隐藏副作用
- 没有“半更新”状态
- 可以无限次重建
这直接解决了 UIKit 中最痛苦的问题:
❌ “现在 UI 是不是和状态一致?”
2️⃣ 没有生命周期陷阱
在 SwiftUI 中:
- 没有
viewDidLoad - 没有
viewWillAppear - 没有“这个 VC 现在活没活着”
因为:
View 本身不承载生命周期,State 才承载
3️⃣ 极强的可预测性(TCA 的根基)
这也是为什么 TCA / Redux 能在 SwiftUI 中如此自然:
Action → State → View
如果 View 是引用类型、可变对象:
- 单一 State 就失效
- 时间旅行就做不到
五、那 @StateObject 为什么“只创建一次”?
这正是 identity 在起作用。
@StateObject var vm = ViewModel()
SwiftUI 规则是:
在相同 View identity 位置,
@StateObject只初始化一次
- View struct 重建 ❌
- ViewModel 实例保留 ✅
这也是为什么你 不能 在子 View 中随便 new ViewModel —— identity 会变。
六、一个类比(非常重要)
把 SwiftUI 想成这样:
View= React / Flutter 里的 Widgetbody= render 函数- State = 外部 Store
- Render Tree = UIKit / AppKit 对象树
你写的不是 UI 本体,而是:
“状态 → UI 的映射关系”
七、最终总结(一句话版)
SwiftUI 中 UI 的“持续存在”,不是因为 View 活着,而是因为 State + Identity + Render Tree 活着;
View 只是一个随时可被丢弃、随时可重建的描述值。
英文版
6-7. [Architecture Design] SwiftUI Views are Value Types, but UI is "Persistent"—How Does This Work?
One-Sentence Conclusion (Intuition First)
A SwiftUI View is not the UI itself, but a "blueprint" (instruction manual) on how to build the UI; what is truly "persistent" is a UI state tree (Render Tree) maintained by SwiftUI behind the scenes.
Therefore:
- View is a value type and can be discarded at any time.
- UI persistence comes from State + Identity + Diffing algorithm, not from the View instance itself.
I. Deconstructing the Biggest Misconception
- ❌ Wrong Intuition (UIKit Thinking):
View struct= The actual UI element on the screen. - ✅ The Real SwiftUI Model:
View struct= A function/recipe describing the UI.UI Entity= The Render Tree managed by the SwiftUI Runtime.
When you write:
Swift
struct ContentView: View {
var body: some View {
Text("Hello")
}
}
You are not "creating a Text object." You are saying: "Under the current state, the UI should look like this."
II. What is Truly "Persistent" in SwiftUI?
It’s not the View, but three specific things:
-
State:
@State,@StateObject, etc., are not stored inside the View struct.- The SwiftUI Runtime stores them in an independent State Store.
- Even if the View struct is destroyed and rebuilt, the State Store remains.
-
Identity:
- SwiftUI uses identity to know: "Is this the same UI element or a new one?"
- Identity comes from the View's position in the tree, explicit
.id(...)modifiers, or stable IDs in aForEach. - This ensures that the "new" View generated matches the "old" one.
-
Render Tree:
- SwiftUI maintains a hidden UI tree composed of reference types (similar to UIKit’s
UIViewtree), which handles the actual rendering. You never touch this directly.
- SwiftUI maintains a hidden UI tree composed of reference types (similar to UIKit’s
III. What Happens When State Changes? (The Workflow)
Imagine a CounterView with a count state. When you click a button:
- State Change: The
countin the State Store updates from 0 to 1. - Re-evaluating Body: SwiftUI calls the
bodyproperty again, generating a new View struct value. Note: It doesn't "update" the old View; it recalculates the description. - Diffing: SwiftUI compares the previous View description with the new one. It detects that only the
Textcontent has changed. - Minimal Update: It updates only the specific node in the Render Tree that corresponds to the Text.
👉 The UI appears "persistent," but it is actually being precisely replacement.
IV. Why are Value Types an Advantage?
- View as a Pure Function:
View = f(State). There are no hidden side effects or "half-updated" states. It can be rebuilt infinitely without cost. - No Lifecycle Traps: There is no
viewDidLoadorviewWillAppear. You don't need to worry if a ViewController is "alive" because the View itself doesn't carry a lifecycle; the State does. - High Predictability: This is the foundation of architectures like TCA. The flow of
Action → State → Viewworks perfectly because Views are immutable descriptions.
V. Why is @StateObject Created Only Once?
This is where Identity plays its role.
Swift
@StateObject var vm = ViewModel()
SwiftUI’s rule: At the same View Identity position, a @StateObject is initialized only once. The View struct might be rebuilt a hundred times, but the ViewModel instance is preserved by the Runtime.
VI. An Analogy (Crucial)
Think of SwiftUI like this:
- View = A Widget in React/Flutter.
- body = The
renderfunction. - State = An external Store.
- Render Tree = The actual UIKit/AppKit object tree.
You aren't writing the UI body; you are writing the Mapping Relationship between State and UI.
VII. Final Summary
In SwiftUI, UI "persistence" exists not because the View stays alive, but because the State, Identity, and Render Tree stay alive. The View is merely a disposable description value that can be discarded and rebuilt at any time.