UIImage导致UITableView渲染卡顿 如何优化性能提升滚动流畅度?
问题根因
当前代码存在三个核心性能问题直接导致滚动卡顿:
- 同步在主线程执行网络请求:
NSData(contentsOf: url)是同步方法,会直接卡住主线程等待网络返回,滚动时频繁调用必然产生严重掉帧 - 主线程实时执行图片重绘缩放:图片缩放是CPU密集型操作,每次cell复用的时候都在主线程做重绘,占用大量主线程计算资源
- 无任何缓存逻辑:同一张图片滚动出可视区域再滚回来,会重复执行下载、缩放逻辑,产生大量无效性能开销
另外手动缩放图片适配UIImageView的操作完全是多余的,系统本身提供了contentMode属性可以自动完成图片适配,不需要手动重绘生成新图。
优化方案
- 所有耗时操作异步执行:网络请求、图片缩放逻辑全部移到后台子线程执行,处理完成后再切回主线程赋值给UIImageView
- 增加双层缓存:内存缓存存储已经处理好的适配后图片,同URL图片直接读取内存缓存即可使用;磁盘缓存存储原始下载的图片数据,无内存缓存时不需要重新发起网络请求
- 提前预计算cell高度:请求到帖子数据时就提取图片的宽高比,在
tableView(_:heightForRowAt:)方法中直接根据屏宽计算好图片高度和cell总高度返回,从根源解决cell高度错乱问题,不需要依赖图片加载完成后再调整布局 - 替换网络请求方式:废弃
NSData(contentsOf: url)同步网络请求,改用URLSession异步任务实现网络请求,避免主线程阻塞 - 移除手动图片重绘逻辑:UIImageView的contentMode设置为
.scaleAspectFill,加上约束固定图片的宽度、顶部/底部间距,系统会自动适配图片展示,不需要手动生成缩放后的图片
优化后核心逻辑示例
// 建议写在PostTableViewCell内部,cell复用的时候自动处理旧请求的校验 func set(post: Post) { self.post = post // 先重置图片,避免复用显示旧图 self.MediaPhoto.image = nil // 先查内存缓存有没有处理好的图片 if let cachedImage = ImageCache.shared.memoryCache.object(forKey: post.mediaURL as NSString) { self.MediaPhoto.image = cachedImage return } // 后台线程发起异步请求 DispatchQueue.global().async { guard let url = URL(string: post.mediaURL), let imageData = try? Data(contentsOf: url), let originalImage = UIImage(data: imageData) else { return } // 后台线程处理图片缩放 let targetWidth = UIScreen.main.bounds.width - 32 // 提前算好你的图片目标宽度 let scale = targetWidth / originalImage.size.width let targetHeight = originalImage.size.height * scale let renderFormat = UIGraphicsImageRendererFormat() renderFormat.scale = UIScreen.main.scale let resizedImage = UIGraphicsImageRenderer(size: CGSize(width: targetWidth, height: targetHeight), format: renderFormat).image { context in originalImage.draw(in: CGRect(origin: .zero, size: CGSize(width: targetWidth, height: targetHeight))) } // 存缓存 ImageCache.shared.memoryCache.setObject(resizedImage, forKey: post.mediaURL as NSString) // 切回主线程赋值 DispatchQueue.main.async { // 校验当前cell对应的post是不是还是原来的,避免复用错乱 guard self.post?.mediaURL == post.mediaURL else { return } self.MediaPhoto.image = resizedImage } } } // 简单的内存缓存实现示例 class ImageCache { static let shared = ImageCache() let memoryCache: NSCache<NSString, UIImage> = { let cache = NSCache<NSString, UIImage>() cache.countLimit = 100 // 最多缓存100张图,可根据需求调整 return cache }() private init() {} }
内容的提问来源于stack exchange,提问作者CodingForFun333
相关产品推荐
相关产品推荐

