Swift Continuations 完全指南:一口气弄懂 4 种“桥梁”

一、为什么需要 Continuations?

Swift 5.5 带来 async/await,但:

  • 老 SDK / 三方库仍用回调
  • 自己封装的 DispatchQueueTimerNotificationCenter 也是回调

Continuation 就是"回调 → async"的官方桥梁,

核心思想:"记住挂起点,等回调来了再恢复。"

二、4 种函数一张表

函数 是否检查误用 是否支持 throws 典型用途
withUnsafeContinuation 性能敏感、自己保证只 resume 一次
withCheckedContinuation 开发阶段、调试逻辑
withUnsafeThrowingContinuation 老回调用 Result/Error
withCheckedThrowingContinuation 推荐默认,又能抛又能查

口诀:"开发用 Checked,上线改 Unsafe;要抛错就带 Throwing。"

三、最小例子:把 DispatchQueue 计时器变成 async

老回调版:

swift 复制代码
func oldTimer(seconds: Int, completion: @Sendable @escaping () -> Void) {
    DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(seconds)) {
        completion()
    }
}
  1. unsafe 版(最简)
swift 复制代码
func modernTimer(seconds: Int) async {
    await withUnsafeContinuation { continuation in
        oldTimer(seconds: seconds) {
            continuation.resume()          // 必须且只能调一次
        }
    }
}
  1. checked 版(开发推荐)
swift 复制代码
func safeTimer(seconds: Int) async {
    await withCheckedContinuation { continuation in
        oldTimer(seconds: seconds) {
            continuation.resume()
            // 如果这里手滑再 resume 一次 → 运行时直接 fatal + 清晰提示
        }
    }
}

错误示例:

vbscript 复制代码
SWIFT TASK CONTINUATION MISUSE: safeTimer(seconds:) tried to resume its continuation more than once

四、Throwing 版本:Network 回调 → async throws

老接口:

swift 复制代码
struct User {
    let name: String
    init(name: String) {
        self.name = name
    }
}

enum NetworkError: Error { case invalidID }

func fetchUser(id: String, completion: @escaping (Result<User, Error>) -> Void) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 0.3) {
        id == "invalid" ? completion(.failure(NetworkError.invalidID))
                        : completion(.success(User(name: "A")))
    }
}
  1. throwing + checked(默认选它)
swift 复制代码
func modernFetch(id: String) async throws -> User {
    try await withCheckedThrowingContinuation { continuation in
        fetchUser(id: id) { result in
            switch result {
            case .success(let user):
                continuation.resume(returning: user)
            case .failure(let error):
                continuation.resume(throwing: error)
            }
        }
    }
}

// 使用
Task {
    do {
        let user = try await modernFetch(id: "123")
        print("Got", user.name)
    } catch {
        print("Error:", error)
    }
}
  1. 如果回调用两个单独参数 (Data?, Error?)
swift 复制代码
continuation.resume(
    with: Result(success: data, failure: error)   // 方便构造
)

五、Actor 隔离专用参数 isolation

场景:在 @MainActor 类里发起网络,回来必须主线程刷新 UI。

swift 复制代码
@MainActor
class VM {
    let label = UILabel()

    func updateText() async {
        let text = try? await withCheckedThrowingContinuation(
            isolation: MainActor.shared          // 👈 指定"恢复点"隔离域
        ) { continuation in
            fetchString { continuation.resume(returning: $0) }
        }
        label.text = text                        // 保证在主线程
    }
}
  • isolation: 让 resume 后的代码直接跑到指定 Actor
  • 省去一次 await MainActor.run { } 切换,更轻量
  • 仅 Throwing 系列支持(Swift 6.0+)

六、常见坑 & Tips

  1. 必须且只能 resume 一次

    忘记 → 任务永远挂起;多调 → 运行时 fatal

  2. 不要存储 continuation 到外部属性

    生命周期仅限 {} 块,出来就 UB。

  3. Task 取消感知

    若业务需支持取消,请用 withTaskCancellationHandlerAsyncStream

  4. 闭包捕获 self 小心循环引用

    老回调里 weak self 的习惯继续保持。

七、一句话总结

Continuation = 回调 → async/await 的"官方翻译器"。

记住口诀:

"开发用 Checked,上线 Unsafe;要抛错就 Throwing;主线程回来加 isolation。"

用好这把桥,就能一口气把全项目的老回调全部现代化,同时享受编译期检查和运行时性能。

相关推荐
语歌2 小时前
AI 语言学习系统的工程边界:为什么 LLM 不该负责复习排期
人工智能·swift
2501_916008891 天前
iOS应用开发工具全面解析:如何选择与优化开发效率
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
大龄秃头程序员1 天前
Swift 属性包装器进阶:从“语法糖”到“并发安全”的 5 个深坑
swift
Lvan的前端笔记2 天前
SwiftUI:iOS 常用视图组件速查
ios·swiftui·swift
智购科技无人售货机工厂2 天前
自动售货机硬件选型避坑:单片机 / 树莓派 / ESP32 怎么选?~YH
人工智能·单片机·嵌入式硬件·r语言·swift·perl·symfony
游戏开发爱好者82 天前
iOS开发IDE有哪些 Xcode 和 快蝎 轻量替代方案
ide·vscode·ios·个人开发·xcode·swift·敏捷流程
2501_915921433 天前
从零开始学 Swift iOS 开发 iOS应用入门
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
末代iOS程序员华仔3 天前
Swift 的角色演变:从“主角”到“最佳配角”——iOS 应用被 Flutter 替换,Swift 变成辅助?
flutter·ios·swift
末代iOS程序员华仔4 天前
Cursor + GitOps:自动化运维新姿势
flutter·ios·swift