12-3.【SwiftUI】SwiftUI 的 View 与 UIKit 的 UIViewController 生命周期差异有哪些?

3 阅读3分钟

理解这两者的差异,本质上是理解 “持有对象(UIKit)”“描述状态(SwiftUI)” 之间的思维转换。UIKit 的生命周期围绕着 实例的创建与销毁,而 SwiftUI 的生命周期围绕着 身份(Identity)在渲染树中的存在

以下是它们在生命周期各阶段的深度对比:


1. 生命周期事件对照表

阶段UIKit (UIViewController)SwiftUI (View)
初始化init() (仅一次)init() (极其频繁,每次重绘都执行)
加载完成viewDidLoad()无直接对应 (通常用 .onAppear.task)
即将显示viewWillAppear(_:).onAppear { ... } (近似)
已经显示viewDidAppear(_:)无精确对应 (onAppear 在动画开始前触发)
异步/并发需在 viewDidAppear 自行启动.task { ... } (支持自动取消)
即将/已经消失viewWill/DidDisappear.onDisappear { ... }
布局更新viewWillLayoutSubviews自动处理 (或用 GeometryReader 监听)

2. 核心差异:从“实例化”到“身份化”

UIKit:对象的长跑

在 UIKit 中,UIViewController 是一个 Class(引用类型) 。它一旦被创建,其内存地址就是固定的。

  • 防御式理解:你可以安全地在 viewDidLoad 中设置一次性的监听或资源初始化,因为这个实例在整个页面存续期间不会变。

SwiftUI:结构体的瞬时重造

ViewStruct(值类型) 。每当状态改变,SwiftUI 都会重新调用 init()

  • 警惕点不要在 View 的 init() 里写逻辑! 它是瞬时的。如果你在 init 里初始化一个网络请求,你的 App 会因为视图的频繁刷新而产生数千个冗余请求。

3. 三个关键的生命周期陷阱

A. onAppear 不等于 viewDidAppear

  • 在 UIKit 中,viewDidAppear 保证了视图已经在屏幕上绘制完成。
  • 在 SwiftUI 中,.onAppear 的触发时机可能早于视图渲染完成。如果你的逻辑依赖于精确的 UI 尺寸,.onAppear 可能会拿到错误的 frame

B. .task 的革命性优势

UIKit 中处理异步任务很头疼:你需要在 viewDidAppear 开启,在 viewWillDisappear 手动取消,否则会造成内存泄漏或闭包回调崩溃。

  • SwiftUI 方案.task 钩子不仅在视图出现时启动异步任务,更会在视图从渲染树中移除时 自动发送取消信号。这是防御式异步编程的最佳实践。

C. @StateObject:生命周期的锚点

既然 View Struct 总是被销毁重造,状态怎么活下来?

  • SwiftUI 引入了 @StateObject。即使 View 的 init() 跑了 100 次,StateObject 的实例只会在该 View 第一次进入 Attribute Graph 时创建一次,并直到 View 彻底从渲染树移除才销毁。

4. 开发建议:如何平滑过渡?

如果你习惯了 UIKit 的思维,在 SwiftUI 中请遵循以下原则:

  1. 逻辑下沉到 ViewModel:将原本写在 viewDidLoad 的初始化逻辑移到 ObservableObjectinit.task 中。
  2. 避免依赖 init 参数更新:因为 init 频繁调用,如果参数里有复杂的计算,会导致性能瓶颈。
  3. 拥抱 .onChange:代替 UIKit 中的“观察者模式”,直接监听状态变化来触发副作用。

总结:UIKit 生命周期是在管理一个 “有生命的实体” ,而 SwiftUI 是在管理一个 “身份的在线状态”