题目
优化UITableView中图片加载导致的卡顿问题
信息
- 类型:问答
- 难度:⭐
考点
异步加载, 图片缓存, 单元格复用
快速回答
解决UITableView图片加载卡顿的核心方法:
- 使用异步加载避免阻塞主线程
- 实现内存缓存和磁盘缓存减少重复下载
- 正确处理单元格复用时的图片请求
- 对图片进行尺寸压缩和解码优化
问题原因
当在UITableViewCell中直接同步加载或下载大尺寸图片时,会导致:
- 主线程被阻塞造成滚动卡顿
- 重复下载相同图片浪费资源
- 未处理的单元格复用引发图片错乱
解决方案与代码示例
1. 异步加载(核心)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let imageUrl = dataArray[indexPath.row].imageUrl
// 先设置占位图
cell.imageView?.image = UIImage(named: "placeholder")
// 异步加载
DispatchQueue.global().async {
guard let url = URL(string: imageUrl),
let data = try? Data(contentsOf: url),
let image = UIImage(data: data) else { return }
// 主线程更新UI
DispatchQueue.main.async {
// 验证单元格是否仍可见
if let updateCell = tableView.cellForRow(at: indexPath) {
updateCell.imageView?.image = image
updateCell.setNeedsLayout()
}
}
}
return cell
}2. 图片缓存(NSCache示例)
class ImageCache {
static let shared = NSCache<NSString, UIImage>()
}
// 使用缓存
if let cachedImage = ImageCache.shared.object(forKey: imageUrl as NSString) {
cell.imageView?.image = cachedImage
} else {
// 异步下载并缓存
downloadImageAndCache(url: imageUrl, for: indexPath)
}3. 单元格复用处理
// 在cellForRowAt开始时取消旧请求
cell.tag = indexPath.row
// 在异步回调中检查
DispatchQueue.main.async {
if cell.tag == indexPath.row { // 验证单元格未复用
cell.imageView?.image = image
}
}最佳实践
- 使用第三方库:SDWebImage或Kingfisher自动处理异步/缓存/复用
- 图片压缩:请求服务端返回合适尺寸的图片
- 解码优化:在后台线程进行UIGraphicsImageRenderer绘制
- 预加载:在cellWillDisplay时提前加载下一页数据
常见错误
- 在主线程同步下载图片(致命错误)
- 未处理单元格复用导致图片错乱
- 缓存无限增长引发内存警告
- 加载原始大图(应使用缩略图)
扩展知识
- 磁盘缓存:使用URLCache或FileManager持久化存储
- 渐进式加载:先显示模糊图再逐渐清晰
- RunLoop优化:在DefaultMode加载图片,滚动时暂停任务
- 矢量图替代:简单图标使用PDF/SFSymbol减少资源体积