Swift并发编程演进

Swift 5.5引入了全新的并发编程模型,通过async/await语法彻底改变了iOS/macOS开发中的异步处理方式。相比传统的GCD和闭包回调,新的并发模型提供了更清晰的代码结构和更强的类型安全。

传统并发方式的痛点

  • 回调地狱:多层嵌套闭包导致代码难以阅读和维护
  • 错误处理复杂:异步错误传递需要额外的Result类型封装
  • 状态管理困难:并发操作的状态同步容易出错
  • 编译器无法优化:缺乏结构化并发支持

async/await基础

async/await是Swift并发编程的核心语法,让异步代码看起来像同步代码一样直观。

定义异步函数

// 使用async标记异步函数
func fetchUserData(userId: String) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(userId)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// 并发执行多个请求
func fetchMultipleUsers(userIds: [String]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        for userId in userIds {
            group.addTask {
                try await fetchUserData(userId: userId)
            }
        }
        
        var users: [User] = []
        for try await user in group {
            users.append(user)
        }
        return users
    }
}
最佳实践:使用withThrowingTaskGroup可以并发执行多个独立任务,并自动处理错误传播。

Actor与数据隔离

Actor是Swift并发模型中的重要概念,用于保护可变状态免受数据竞争的影响。

定义Actor

// 定义一个Actor来管理共享状态
actor UserCache {
    private var cache: [String: User] = [:]
    
    func getUser(id: String) -> User? {
        return cache[id]
    }
    
    func setUser(_ user: User, for id: String) {
        cache[id] = user
    }
    
    func clearCache() {
        cache.removeAll()
    }
}

// 使用Actor
let cache = UserCache()

// 自动串行访问,避免数据竞争
await cache.setUser(user, for: "123")
let cachedUser = await cache.getUser(id: "123")
注意:Actor内部的方法调用是串行的,外部访问必须通过await。这保证了同一时间只有一个任务可以修改Actor的状态。

结构化并发

结构化并发确保子任务的生命周期与父任务绑定,避免任务泄漏。

Task与TaskGroup

// 创建独立任务
let task = Task {
    await fetchUserData(userId: "123")
}

// 取消任务
task.cancel()

// 使用TaskGroup进行结构化并发
func processImages(imageURLs: [URL]) async -> [UIImage] {
    await withTaskGroup(of: UIImage?.self) { group in
        for url in imageURLs {
            group.addTask {
                await downloadImage(from: url)
            }
        }
        
        var images: [UIImage] = []
        for await image in group {
            if let image = image {
                images.append(image)
            }
        }
        return images
    }
}

MainActor与UI更新

在iOS开发中,UI更新必须在主线程执行。MainActor简化了这一操作。

// 自动在主线程执行
@MainActor
func updateUI(with user: User) {
    nameLabel.text = user.name
    avatarImageView.image = user.avatar
}

// 或者使用await切换回主线程
func fetchAndDisplayUser() async {
    let user = await fetchUserData(userId: "123")
    await MainActor.run {
        updateUI(with: user)
    }
}
提示:SwiftUI的View协议默认带有@MainActor属性,因此body属性内的代码自动在主线程执行。

实战案例:网络请求管理器

下面是一个完整的网络请求管理器实现,展示了Swift并发特性的综合应用。

actor NetworkManager {
    private let session: URLSession
    private var activeTasks: [String: Task] = [:]
    
    init(session: URLSession = .shared) {
        self.session = session
    }
    
    func request(_ endpoint: Endpoint) async throws -> T {
        // 取消相同标识的正在进行的请求
        if let existingTask = activeTasks[endpoint.identifier] {
            existingTask.cancel()
        }
        
        let task = Task {
            defer { activeTasks.removeValue(forKey: endpoint.identifier) }
            
            let (data, response) = try await session.data(for: endpoint.request)
            
            guard let httpResponse = response as? HTTPURLResponse else {
                throw NetworkError.invalidResponse
            }
            
            guard (200...299).contains(httpResponse.statusCode) else {
                throw NetworkError.httpError(statusCode: httpResponse.statusCode)
            }
            
            return data
        }
        
        activeTasks[endpoint.identifier] = task
        
        let data = try await task.value
        return try JSONDecoder().decode(T.self, from: data)
    }
    
    func cancelAllRequests() {
        activeTasks.values.forEach { $0.cancel() }
        activeTasks.removeAll()
    }
}

// 使用示例
struct UserService {
    private let networkManager = NetworkManager()
    
    func getUser(id: String) async throws -> User {
        try await networkManager.request(.user(id: id))
    }
}

性能优化建议

场景 推荐方案 说明
独立异步操作 async/await 代码清晰,错误处理简单
多个独立请求 TaskGroup 并发执行,自动等待全部完成
共享可变状态 Actor 自动数据隔离,防止竞争
UI更新 @MainActor 确保主线程执行
后台任务 Task.detached 脱离当前Actor上下文

总结

Swift的并发编程模型通过async/await、Actor和结构化并发,为iOS/macOS开发提供了现代化的异步编程方案。相比GCD,新的并发模型具有以下优势:

  • 代码可读性显著提升,告别回调地狱
  • 编译器级别的数据竞争检测
  • 结构化并发避免任务泄漏
  • 与Swift类型系统深度集成

建议在新项目中优先使用Swift并发特性,逐步迁移旧代码以获得更好的开发体验。