场景:同步上下文想调异步函数
swift
func fetchData() async -> String { /* 网络请求 */ }
struct ContentView: View {
var body: some View {
Button("Load") {
await fetchData() // ❌ 编译错误:同步闭包里不能用 await
}
}
}
错误提示:
Cannot pass function of type '() async -> Void' to parameter expecting synchronous function type
官方逃生舱:包一层 Task {}
swift
Button("Load") {
Task { // ✅ 立即启动异步任务
let data = await fetchData()
print(data)
}
}
- Task 会立刻在新协程里执行闭包,不要求外部上下文支持并发。
- 无需手动持有 Task 实例,除非你想取消或等待它完成。
Task 的 3 种常见"同步→异步"桥梁模式
模式 | 代码片段 | 用途 |
---|---|---|
Fire-and-forget | Task { await work() } |
按钮点击、日志上报 |
取消友好 | Task { [weak self] in ... } |
ViewController/View 生命周期 |
Delegate/App 生命周期 | Task { await requestPermissions() } |
application(_:didFinishLaunchingWithOptions:) |
实战 1:带取消的 SwiftUI 任务
swift
struct ContentView: View {
@State private var task: Task<Void, Never>? // 1️⃣ 持有任务
var body: some View {
VStack {
Button("Start") {
task = Task { // 2️⃣ 创建并保存
let data = await fetchData()
print(data)
}
}
Button("Cancel") {
task?.cancel() // 3️⃣ 外部取消
task = nil
}
}
.onDisappear {
task?.cancel() // 4️⃣ 生命周期清理
}
}
}
记住:视图消失时必须取消,否则后台任务可能访问已销毁的
@State
。
实战 2:AppDelegate 里请求推送权限
swift
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// 同步方法内直接启动异步任务
Task {
do {
try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound])
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
} catch {
print("权限请求失败: \(error)")
}
}
return true
}
}
Task
让"老派"的同步生命周期钩子也能享受结构化并发。- 不需要
await
在application(_:didFinishLaunchingWithOptions:)
返回值里,启动即忘即可。
闭包捕获注意事项
swift
Button("Load") {
Task { [weak self] in // ✅ 防止循环引用
guard let self else { return }
self.model.data = await self.fetchData()
}
}
在 View/ViewController/ViewModel 里使用
Task
时,养成[weak self]
习惯,避免闭包持有整个视图层级。
一句话总结
"同步上下文想 await?包一层
Task {}
就行。"
它是 Swift 结构化并发的任意门:轻量、无样板、可取消。
只要记得生命周期对齐 + 弱引用捕获,就能在按钮、Delegate、App 生命周期里放心使用 async/await。