并发与 async/await
Task、Actor、SwiftUI 异步加载。
35 分钟
并发与 async/await
Swift 并发模型:async/await、Task、Actor 保证线程安全。对照 /roadmaps/swift-ui「并发」节点,网络加载与 UI 更新必须正确分离主线程与后台任务。
学习目标
- 理解
@MainActor与主线程 UI 更新规则 - 用
Task、TaskGroup执行异步工作 - 掌握 Actor 隔离可变状态
- 在 SwiftUI 中用
.task与 async ViewModel 加载数据
核心概念
async/await 与 MainActor
struct Weather: Decodable {
let city: String
let temp: Double
let condition: String
}
enum WeatherError: Error {
case networkFailed
}
func fetchWeather(city: String) async throws -> Weather {
try await Task.sleep(nanoseconds: 500_000_000)
return Weather(city: city, temp: 22.5, condition: "晴")
}
@MainActor
final class WeatherViewModel: ObservableObject {
@Published var weather: Weather?
@Published var isLoading = false
@Published var errorMessage: String?
func load(city: String) async {
isLoading = true
errorMessage = nil
do {
weather = try await fetchWeather(city: city)
} catch {
errorMessage = "加载失败"
}
isLoading = false
}
}
struct WeatherView: View {
@StateObject private var viewModel = WeatherViewModel()
@State private var city = "北京"
var body: some View {
VStack(spacing: 16) {
TextField("城市", text: $city)
.textFieldStyle(.roundedBorder)
if viewModel.isLoading {
ProgressView("加载中…")
} else if let w = viewModel.weather {
Text("\(w.city) \(w.temp, specifier: "%.1f")°C")
Text(w.condition).foregroundStyle(.secondary)
}
Button("刷新") {
Task { await viewModel.load(city: city) }
}
}
.padding()
.task(id: city) { await viewModel.load(city: city) }
}
}
@MainActor 保证 @Published 变更在主线程;ViewModel 标注 @MainActor 是 SwiftUI 最佳实践。
Actor 与 TaskGroup
actor ImageCache {
private var storage: [URL: Data] = [:]
func data(for url: URL) -> Data? {
storage[url]
}
func store(_ data: Data, for url: URL) {
storage[url] = data
}
}
func fetchMultiple(urls: [URL]) async -> [Data] {
await withTaskGroup(of: Data?.self) { group in
for url in urls {
group.addTask {
try? Data(contentsOf: url)
}
}
var results: [Data] = []
for await data in group {
if let data { results.append(data) }
}
return results
}
}