本文通过对比传统闭包回调与Swift现代并发模型的实现差异,提供一套完整的代码迁移方案。你将掌握如何将网络请求、并行任务、数据流处理和线程安全机制全面升级到async/await范式,提升代码可读性和性能。
一、网络请求改造:消除回调地狱
传统闭包实现(金字塔问题)
func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
let request = thumbnailURLRequest(for: id)
let task = URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else {
completion(nil, error)
return
}
UIImage(data: data)?.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
guard let thumbnail = thumbnail else {
completion(nil, FetchError.badImage)
return
}
completion(thumbnail, nil)
}
}
task.resume()
}
现代async/await方案(线性执行)
func fetchThumbnail(for id: String) async throws -> UIImage {
let request = thumbnailURLRequest(for: id)
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw FetchError.badID
}
guard let image = UIImage(data: data),
let thumbnail = await image.byPreparingThumbnail(ofSize: CGSize(width: 40, height: 40))
else {
throw FetchError.badImage
}
return thumbnail
}
关键改造点:
- 使用
await替代dataTask闭包 - 将嵌套缩略图生成闭包改为同步等待
- 错误处理升级为throw机制
二、并行任务处理:Task Group最佳实践
传统串行下载
// 存在顺序执行的性能瓶颈
func fetchAllThumbnails(ids: [String], completion: @escaping ([UIImage]) -> Void) {
var results = [UIImage]()
var remaining = ids.count
for id in ids {
fetchThumbnail(for: id) { image, _ in
if let image = image {
results.append(image)
}
remaining -= 1
if remaining == 0 {
completion(results)
}
}
}
}
并行优化实现
func fetchThumbnails(ids: [String]) async throws -> [UIImage] {
try await withThrowingTaskGroup(of: UIImage.self) { group in
// 批量添加并发任务
ids.forEach { id in
group.addTask { try await self.fetchThumbnail(for: id) }
}
// 实时收集结果
return try await group.reduce(into: []) { $0.append($1) }
}
}
性能提升点:
- 并行执行所有下载任务
- 使用
reduce自动处理结果收集 - 内置错误传播机制
三、数据流处理:AsyncStream实战升级
闭包式进度监控
class Downloader {
func download(url: URL,
progress: @escaping (Float) -> Void,
completion: @escaping (Result<Data, Error>) -> Void)
}
// 使用示例
Downloader().download(url: url,
progress: { print("进度:($0)") },
completion: { result in /*...*/ }
)
异步流式改造
extension Downloader {
func downloadStream(for url: URL) -> AsyncThrowingStream<DownloadStatus, Error> {
AsyncThrowingStream { continuation in
do {
try self.download(url: url,
progress: { progress in
continuation.yield(.progress(progress))
},
completion: { result in
switch result {
case .success(let data):
continuation.yield(.completed(data))
continuation.finish()
case .failure(let error):
continuation.finish(throwing: error)
}
}
)
} catch {
continuation.finish(throwing: error)
}
}
}
}
// 消费数据流
do {
for try await status in downloader.downloadStream(for: url) {
switch status {
case .progress(let value):
print("下载进度:(value * 100)%")
case .completed(let data):
print("完成下载,数据大小:(data.count)字节")
}
}
} catch {
print("下载失败:(error)")
}
核心优势:
- 统一处理进度和完成事件
- 支持
for await循环实时消费数据 - 错误自动在循环外捕获
四、线程安全:从GCD到Actor的迁移
传统队列方案
class UserDatabase {
private var storage = [String: User]()
private let queue = DispatchQueue(label: "user.db.queue")
func store(_ user: User) {
queue.async { self.storage[user.id] = user }
}
func load(id: String) -> User? {
queue.sync { storage[id] }
}
}
Actor安全实现
actor UserDatabase {
private var storage = [String: User]()
func store(_ user: User) {
storage[user.id] = user
}
func load(id: String) -> User? {
storage[id]
}
}
// 安全调用
let db = UserDatabase()
await db.store(user)
let loadedUser = await db.load(id: "123")
重大改进:
- 编译器保证线程安全
- 消除手动队列管理
- 支持await挂起语义
五、桥接旧代码:CheckedContinuation妙用
闭包接口现代化改造
// 传统回调方法
func fetchMessages(completion: @escaping ([Message]) -> Void) {
URLSession.shared.dataTask(with: messagesURL) { data, _, _ in
let messages = (try? JSONDecoder().decode([Message].self, from: data ?? Data())) ?? []
completion(messages)
}.resume()
}
// 转换为async接口
func fetchMessages() async -> [Message] {
await withCheckedContinuation { continuation in
fetchMessages { messages in
continuation.resume(returning: messages)
}
}
}
// 新调用方式
let messages = await fetchMessages()
print("获取到(messages.count)条消息")
关键步骤:
- 使用
withCheckedContinuation包装旧接口 - 在回调中恢复continuation
- 保持原有错误处理逻辑
六、主线程安全:@MainActor实践
UI更新最佳实践
@MainActor
func updateUI(with messages: [Message]) {
tableView.reloadData()
unreadBadge.text = "(messages.count)"
}
// 自动调度到主线程
Task {
let messages = await fetchMessages()
await updateUI(with: messages)
}
CLLocationManager桥接
@MainActor
class LocationService: NSObject, CLLocationManagerDelegate {
private var continuation: CheckedContinuation<CLLocation, Error>?
private let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
}
func currentLocation() async throws -> CLLocation {
try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
manager.requestLocation()
}
}
// Delegate实现
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
continuation?.resume(returning: locations)
continuation = nil
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
continuation?.resume(throwing: error)
continuation = nil
}
}
迁移收益总结
通过本文的改造方案,您的代码将获得:
- 可读性提升:消除嵌套闭包,代码执行流清晰可见
- 性能优化:Task Group实现真正的并行计算
- 安全性增强:Actor机制杜绝数据竞争
- 维护成本降低:统一错误处理,减少回调管理
- 现代API兼容:无缝接入SwiftUI、Combine等新框架
建议采用渐进式迁移策略,优先改造核心业务模块,逐步享受Swift并发编程带来的开发红利。