题目
实现一个支持缓存和错误处理的异步图片加载组件
信息
- 类型:问答
- 难度:⭐⭐
考点
异步加载, 状态管理, 缓存机制, 错误处理, SwiftUI视图更新
快速回答
实现一个可重用的网络图片加载组件需要:
- 使用
AsyncImage结合自定义加载器 - 实现
URLCache磁盘缓存机制 - 管理加载过程中的不同状态(加载中/成功/失败)
- 添加占位符和错误回退视图
- 处理图片尺寸自适应和内存管理
核心需求
在SwiftUI中实现一个可重用的图片加载组件,需要解决:网络请求异步性、缓存优化、错误处理、视图状态管理等问题。
实现方案
1. 基础结构(状态管理)
enum LoadState {
case loading
case success(UIImage)
case failure(Error)
}
struct CachedAsyncImage: View {
@State private var state: LoadState = .loading
let url: URL
var body: some View {
Group {
switch state {
case .loading:
ProgressView()
case .success(let image):
Image(uiImage: image)
.resizable()
case .failure:
Image(systemName: "photo")
}
}
.task { await loadImage() }
}
}2. 核心加载逻辑(含缓存)
private extension CachedAsyncImage {
func loadImage() async {
// 检查内存缓存
if let cached = ImageCache.shared.get(forKey: url.absoluteString) {
state = .success(cached)
return
}
do {
// 网络请求
let (data, _) = try await URLSession.shared.data(from: url)
if let image = UIImage(data: data) {
// 缓存并更新状态
ImageCache.shared.set(image, forKey: url.absoluteString)
state = .success(image)
} else {
throw URLError(.badServerResponse)
}
} catch {
state = .failure(error)
}
}
}
// 缓存类实现
final class ImageCache {
static let shared = ImageCache()
private let cache = NSCache<NSString, UIImage>()
private init() {}
func get(forKey key: String) -> UIImage? {
return cache.object(forKey: key as NSString)
}
func set(_ image: UIImage, forKey key: String) {
cache.setObject(image, forKey: key as NSString)
}
}3. 完整组件优化
struct CachedAsyncImage: View {
// ...(同前)
// 添加自定义配置参数
var placeholder: Image = Image(systemName: "photo")
var errorImage: Image? = nil
var body: some View {
Group {
switch state {
case .loading:
placeholder
.resizable()
case .success(let image):
Image(uiImage: image)
.resizable()
case .failure:
errorImage ?? placeholder
}
}
.onAppear { if Task.isCancelled { Task { await loadImage() } } }
.onDisappear { /* 可添加取消逻辑 */ }
}
}
// 使用示例
struct ContentView: View {
var body: some View {
CachedAsyncImage(url: URL(string: "https://example.com/image.jpg")!)
.aspectRatio(contentMode: .fit)
.frame(width: 200)
}
}最佳实践
- 缓存策略:内存缓存(NSCache)+ 磁盘缓存(URLCache)组合使用
- 线程安全:使用
@MainActor确保UI更新在主线程 - 内存管理:
- NSCache会在内存不足时自动释放
- 大图片使用
UIImage(data: data, scale: UIScreen.main.scale)优化
- 扩展性:支持自定义占位符/错误视图/过渡动画
常见错误
- 内存泄漏:未处理Task取消,解决方案:
.task { await loadImage() } .onDisappear { // 取消任务 } - 重复请求:未检查重复URL请求,解决方案:使用
@StateObject管理加载器实例 - 图片尺寸问题:未限制图片大小导致内存溢出,解决方案:添加
.frame限制或使用缩略图URL
扩展知识
- 磁盘缓存:使用
URLCache实现持久化缓存let diskCache = URLCache( memoryCapacity: 100 * 1024 * 1024, diskCapacity: 500 * 1024 * 1024, diskPath: "ImageCache" ) URLSession.shared.configuration.urlCache = diskCache - 高级库对比:SDWebImage(功能全面) vs Kingfisher(轻量) vs Nuke(高性能)
- iOS 15+优化:直接使用
AsyncImage但需扩展缓存功能