历史包袱:海量第三方 SDK 仍是回调形态
swift
// 某社交 SDK,2025 年依旧没变
func login(
_ platform: String,
completion: @escaping (Result<User, Error>) -> Void
)
痛点:
- 嵌套地狱(Callback Pyramid)
- 错误处理分散
- 难以取消
官方桥接器:withCheckedThrowingContinuation
核心思想
把"回调"转成"Continuation"(续体),让编译器帮你生成 async
上下文。
1 行封装
swift
extension SocialSDK {
static func login(_ platform: String) async throws -> User {
try await withCheckedThrowingContinuation { continuation in
login(platform) { result in
continuation.resume(with: result)
}
}
}
}
调用方瞬间清爽:
swift
let user = try await SocialSDK.login("WeChat")
取消传播
swift
func loginCancelable(_ platform: String) async throws -> User {
try await withTaskCancellationHandler(
operation: {
try await withCheckedThrowingContinuation { con in
let task = SocialSDK.login(platform) { con.resume(with: $0) }
// 注册取消回调
con.onTermination = { _ in task.cancel() }
}
},
onCancel: { SocialSDK.cancelLogin(platform) }
)
}
并发安全:让"函数"跨线程也无锁
- 数据竞争的本质
swift
var counter = 0
DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
counter += 1 // 未保护 → 结果 < 1_000_000
}
- 用
actor
把"状态"封装成"函数"
swift
actor Counter1 {
private var value = 0
func increment() -> Int {
value += 1
return value
}
}
Task {
let counter = Counter1()
await withTaskGroup(of: Void.self) { group in
for _ in 0..<1_000_000 {
group.addTask { _ = await counter.increment() }
}
}
print(await counter.increment()) // 1_000_001
}
- actor 内部任意函数都是"串行队列"语义,外部调用需
await
。 - 编译期保证无锁线程安全。
- 纯函数 +
Sendable
实现"无锁并行"
swift
/// 纯函数:无副作用,仅依赖参数
func hash(_ x: Int) -> Int { x &* 31 &+ 7 }
Task.detached {
/// Int 已遵循 Sendable,可安全跨线程
await withTaskGroup(of: Int.self) { group in
for i in 0..<1_000_000 {
group.addTask { hash(i) }
}
let hashes = await group.reduce(0, +)
}
}
规则:
- 值类型(
Int
、String
、Array
等)默认Sendable
。 - 自定义引用类型需显式
final class Box: Sendable
并保证内部不可变或加锁。
函数式高阶算子:并发版 map / filter / reduce
concurrentMap
swift
extension Sequence {
func concurrentMap<T: Sendable>(
_ transform: @Sendable @escaping (Element) async throws -> T
) async rethrows -> [T] where Element : Sendable {
try await withThrowingTaskGroup(of: (Int, T).self) { group in
// 1. 提交所有任务
for (index, item) in self.enumerated() {
group.addTask { (index, try await transform(item)) }
}
// 2. 收集结果,保持顺序
var pairs = [(Int, T)]()
for try await pair in group { pairs.append(pair) }
return pairs.sorted { $0.0 < $1.0 }.map(\.1)
}
}
}
- 使用:并发下载 100 张缩略图
swift
Task {
let urls = (0..<100).map { "https://picsum.photos/200/300?random=\($0)" }
let _: [Data] = try await urls.concurrentMap { url -> Data in
let (data, _) = try await URLSession.shared.data(from: URL(string: url)!)
return data
}
}
- 自动利用所有 CPU 核心
- 顺序与输入一致,无需二次排序
concurrentCompactMap
/concurrentFilter
同理,留给读者练习。
终极案例:用"函数"写出可组合的并发管道
需求:
- 并发下载 HTML →
- 并发解析标题 →
- 顺序汇总报告
swift
struct Report {
let url: String
let title: String
}
func pipeline(_ urls: [String]) async throws -> [Report] {
try await urls
.concurrentMap { url -> (String, Data) in
let (data, _) = try await URLSession.shared.data(from: URL(string: url)!)
return (url, data)
}
.concurrentMap { (url, data) -> Report in
let html = String(decoding: data, as: UTF8.self)
let title = html.replacingOccurrences(
of: #"<title>(.*?)</title>"#,
with: "$1",
options: .regularExpression
)
return Report(url: url, title: title.trimmingCharacters(in: .whitespacesAndNewlines))
}
}
特点:
- 阶段之间用临时元组传递上下文,避免全局可变状态。
- 任意阶段加
try
即可自动终止后续任务,异常传播靠rethrows
机制。
思维导图(文字版)
swift
回调 → withContinuation → async/await
actor / Sendable → 无锁并发
高阶函数 → concurrentMap → 函数式并行管道
面试速答卡片
问题 | 一句话答案 |
---|---|
旧 SDK 回调如何转 async ? |
withCheckedThrowingContinuation 一键桥接 |
actor 与类最大区别? |
编译期强制串行访问,自动解决数据竞争 |
Sendable 对函数意味着什么? |
闭包及其捕获变量必须线程安全,否则编译报错 |
concurrentMap 会不会乱序? |
内部用索引对还原,保证与输入顺序一致 |