在 SwiftUI
中,视图的生命周期与传统 UIKit
的显式生命周期(如 viewDidLoad, viewDidAppear)有本质区别。
SwiftUI
采用声明式编程范式,视图的生命周期由框架自动管理,开发者主要通过响应状态变化和特定修饰符来参与。
一、核心生命周期阶段
1.视图创建(Initialization)
视图结构体(struct
)被初始化,但此时尚未加载到视图树。
注意:避免在初始化器中执行耗时操作,因为 SwiftUI
可能频繁重建视图。
2.视图渲染(Rendering)
当视图首次显示 或 依赖的 @State
、@ObservedObject
等状态变化时,SwiftUI 调用 body
属性计算视图描述。
视图是值类型:每次状态变化都会生成新的视图结构体(高效轻量,非实际视图销毁)。
3.视图挂载(Appearing) → onAppear
Text("Hello")
.onAppear {
// 视图加载到屏幕时触发(类似 viewDidAppear)
// 常用场景:加载数据、开启监听
}
4.视图卸载(Disappearing) → onDisappear
Text("Hello")
.onDisappear {
// 视图从屏幕移除时触发(类似 viewDidDisappear)
// 常用场景:取消网络请求、清理资源
}
二、状态驱动更新
1.视图是短暂的
SwiftUI 可能频繁销毁和重建视图结构体(非底层渲染视图)。避免依赖初始化器执行关键逻辑,改用 onAppear
。
2.状态管理决定生命周期
-
@State
:视图私有状态,生命周期与视图树一致。 -
@ObservedObject
:外部可观察对象,其生命周期由引用计数管理。 -
@StateObject
:推荐替代 @ObservedObject,确保对象生命周期与视图绑定(iOS 13+)。
3.高效差异化更新
SwiftUI 通过比较视图结构体的值,智能更新实际渲染内容(非整体重建)。
三、高级控制
1. task
修饰符(iOS 15+)
在 SwiftUI 中,task 修饰符(iOS 15+)是管理异步任务生命周期的革命性工具,它通过结构化并发自动处理任务的创建、取消和清理。
task 修饰符自动将异步任务绑定到视图的生命周期:
struct UserView: View {
@State private var userData: User?
var body: some View {
VStack {
if let userData {
Text(userData.name)
}
}
.task { // 自动生命周期管理
do {
// 1. 视图出现时启动任务
userData = try await fetchUserData()
} catch {
print("请求失败:", error)
}
// 2. 视图消失时自动取消任务
}
}
func fetchUserData() async throws -> User {
try await Task.sleep(for: .seconds(2)) // 模拟网络请求
return User(name: "John Doe")
}
}
task四大自动管理特性:
- 自动启动
当视图首次出现(onAppear)时,task 自动执行异步闭包:
- 自动启动
.task {
// 视图出现时自动执行
await loadData()
}
- 自动取消
当视图消失(onDisappear)时,自动取消任务并传播取消信号:
- 自动取消
.task {
do {
// 如果视图在请求完成前消失,此请求会被自动取消
let data = try await URLSession.shared.data(from: url)
} catch {
// 捕获 CancellationError(任务被取消时抛出)
if error is CancellationError {
print("任务已取消")
}
}
}
- 依赖驱动的重启
通过 id 参数响应依赖变化,自动取消旧任务并启动新任务:
- 依赖驱动的重启
.task(id: userId) { // 当 userId 变化时
await fetchUserProfile(userId: userId)
}
/---------------/
✅ userId 变化 → 取消当前任务 → 启动新任务
❌ 没有 id 参数 → 依赖变化时任务继续运行(可能数据过时)
- 子任务自动传播
任务内部的子任务会自动继承取消状态:
- 子任务自动传播
.task {
// 父任务
async let image = loadProfileImage() // 子任务1
async let bio = loadUserBio() // 子任务2
// 视图消失时,所有子任务自动取消
let (finalImage, finalBio) = await (image, bio)
}
自动取消传播机制:
1.当父任务取消时,所有子任务自动收到取消信号;
2.正在执行的 await 点抛出 CancellationError;
3.所有任务层级自动清理资源。
正确实践:
.task {
// 1. 使用支持取消的API
let (data, _) = try await URLSession.shared.data(for: request)
// 2. 每次await后检查取消状态
try Task.checkCancellation()
// 3. 自动在主线程更新UI
self.data = data
}
面试点睛:强调 task 如何解决异步编程的三大痛点:
1.资源泄漏(忘记取消任务)
2.状态爆炸(更新已销毁视图)
3.竞态条件(新旧任务冲突)
2. id
修饰符强制刷新
通过改变视图标识重置生命周期:
ChildView()
.id(UUID()) // 每次标识变化时完全重建
可理解为:释放旧的,创建新的。
四、总结
遵循 SwiftUI 的声明式思维:关注状态变化而非生命周期事件,用 onAppear/onDisappear 处理副作用,用 @StateObject 管理对象生命周期。
完整生命周期流程示例:
struct LifecycleDemo: View {
@State private var isActive = false
var body: some View {
VStack {
Toggle("显示子视图", isOn: $isActive)
if isActive {
ChildView()
.onAppear { print("子视图显示") }
.onDisappear { print("子视图移除") }
}
}
}
}
struct ChildView: View {
init() { print("子视图初始化(可能多次调用)") }
var body: some View { Text("子视图内容") }
}