name: ios-swift-concurrency description: 在iOS应用中实现async/await、Task管理、actors或Combine响应式模式时使用。 allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
iOS - Swift 并发
在Swift中使用async/await、actors和结构化并发实现现代并发模式。
关键概念
Async/Await 基础
// 异步函数声明
func fetchUser(id: String) async throws -> User {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw APIError.invalidResponse
}
return try JSONDecoder().decode(User.self, from: data)
}
// 调用异步函数
func loadUserProfile() async {
do {
let user = try await fetchUser(id: "123")
await MainActor.run {
updateUI(with: user)
}
} catch {
await MainActor.run {
showError(error)
}
}
}
任务管理
class UserViewController: UIViewController {
private var loadTask: Task<Void, Never>?
override func viewDidLoad() {
super.viewDidLoad()
// 创建任务用于异步工作
loadTask = Task {
await loadUserData()
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// 视图消失时取消任务
loadTask?.cancel()
}
private func loadUserData() async {
// 检查是否已取消
guard !Task.isCancelled else { return }
do {
let user = try await fetchUser()
// 再次检查后再更新UI
guard !Task.isCancelled else { return }
await MainActor.run {
displayUser(user)
}
} catch {
// 处理错误
}
}
}
Actors 用于线程安全
actor UserCache {
private var cache: [String: User] = [:]
func user(for id: String) -> User? {
cache[id]
}
func setUser(_ user: User, for id: String) {
cache[id] = user
}
func clear() {
cache.removeAll()
}
}
// 使用示例
let cache = UserCache()
Task {
await cache.setUser(user, for: user.id)
let cached = await cache.user(for: "123")
}
MainActor 用于UI更新
@MainActor
class UserViewModel: ObservableObject {
@Published var user: User?
@Published var isLoading = false
@Published var error: Error?
func loadUser(id: String) async {
isLoading = true
defer { isLoading = false }
do {
user = try await userService.fetchUser(id: id)
} catch {
self.error = error
}
}
}
// 或使用 MainActor.run 进行特定操作
func fetchAndDisplay() async {
let data = await fetchData()
await MainActor.run {
self.displayData(data)
}
}
最佳实践
使用TaskGroup的结构化并发
func fetchAllUserData(userId: String) async throws -> UserProfile {
async let user = fetchUser(id: userId)
async let posts = fetchPosts(userId: userId)
async let followers = fetchFollowers(userId: userId)
// 三个请求并发运行
return try await UserProfile(
user: user,
posts: posts,
followers: followers
)
}
// 用于动态任务数量
func fetchMultipleUsers(ids: [String]) async throws -> [User] {
try await withThrowingTaskGroup(of: User.self) { group in
for id in ids {
group.addTask {
try await fetchUser(id: id)
}
}
var users: [User] = []
for try await user in group {
users.append(user)
}
return users
}
}
使用AsyncSequence处理流
// 自定义异步序列
struct NotificationStream: AsyncSequence {
typealias Element = Notification
let name: Notification.Name
struct AsyncIterator: AsyncIteratorProtocol {
let name: Notification.Name
var iterator: AsyncStream<Notification>.Iterator
mutating func next() async -> Notification? {
await iterator.next()
}
}
func makeAsyncIterator() -> AsyncIterator {
let stream = AsyncStream<Notification> { continuation in
let observer = NotificationCenter.default.addObserver(
forName: name,
object: nil,
queue: nil
) { notification in
continuation.yield(notification)
}
continuation.onTermination = { _ in
NotificationCenter.default.removeObserver(observer)
}
}
return AsyncIterator(name: name, iterator: stream.makeAsyncIterator())
}
}
// 使用示例
for await notification in NotificationStream(name: .userDidLogin) {
handleLogin(notification)
}
使用Continuations处理基于回调的API
func fetchLegacyData() async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
legacyAPI.fetch { result in
switch result {
case .success(let data):
continuation.resume(returning: data)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
// 用于基于委托的API
class LocationManager: NSObject, CLLocationManagerDelegate {
private var locationContinuation: CheckedContinuation<CLLocation, Error>?
func getCurrentLocation() async throws -> CLLocation {
try await withCheckedThrowingContinuation { continuation in
self.locationContinuation = continuation
locationManager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationContinuation?.resume(returning: locations[0])
locationContinuation = nil
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationContinuation?.resume(throwing: error)
locationContinuation = nil
}
}
常见模式
取消处理
func downloadFile(url: URL) async throws -> Data {
var data = Data()
let (bytes, _) = try await URLSession.shared.bytes(from: url)
for try await byte in bytes {
// 协作式取消检查
try Task.checkCancellation()
data.append(byte)
}
return data
}
使用Task进行防抖
class SearchViewModel: ObservableObject {
@Published var searchText = ""
@Published var results: [SearchResult] = []
private var searchTask: Task<Void, Never>?
func search(_ query: String) {
searchTask?.cancel()
searchTask = Task {
// 防抖延迟
try? await Task.sleep(for: .milliseconds(300))
guard !Task.isCancelled else { return }
do {
let results = try await searchService.search(query: query)
guard !Task.isCancelled else { return }
await MainActor.run {
self.results = results
}
} catch {
// 处理错误
}
}
}
}
Combine集成
import Combine
extension Publisher {
func asyncMap<T>(_ transform: @escaping (Output) async -> T) -> AnyPublisher<T, Failure> {
flatMap { value in
Future { promise in
Task {
let result = await transform(value)
promise(.success(result))
}
}
}
.eraseToAnyPublisher()
}
}
// 将异步函数转换为发布者
func userPublisher(id: String) -> AnyPublisher<User, Error> {
Future { promise in
Task {
do {
let user = try await fetchUser(id: id)
promise(.success(user))
} catch {
promise(.failure(error))
}
}
}
.eraseToAnyPublisher()
}
反模式
阻塞主线程
错误做法:
// 不要这样做
func loadData() {
let semaphore = DispatchSemaphore(value: 0)
Task {
data = await fetchData()
semaphore.signal()
}
semaphore.wait() // 阻塞主线程!
}
正确做法:
func loadData() async {
data = await fetchData()
}
忽略取消
错误做法:
func processItems(_ items: [Item]) async {
for item in items {
await process(item) // 从不检查取消
}
}
正确做法:
func processItems(_ items: [Item]) async throws {
for item in items {
try Task.checkCancellation()
await process(item)
}
}
共享可变状态的数据竞争
错误做法:
class Counter {
var count = 0 // 非线程安全!
func increment() {
count += 1
}
}
正确做法:
actor Counter {
var count = 0
func increment() {
count += 1
}
}
相关技能
- ios-swiftui-patterns: 在SwiftUI中使用并发
- ios-uikit-architecture: UIKit中的异步模式