横向CollectionView滚动时卡顿崩溃,报内存不足终止错误
兄弟,我太懂这种滚动到一半突然崩掉的糟心感了!结合你用Kingfisher加载图片的场景,大概率是CollectionView的图片加载/复用环节出了内存泄漏或者资源占用过高的问题,给你几个针对性的解决办法:
1. 先把Cell复用和清理做对,这是最常见的坑
首先检查你在collectionView(_:cellForItemAt:)里的cell复用逻辑——绝对不要每次都创建新Cell,必须用复用池的cell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionCell", for: indexPath) as! FilterCollectionCell // 这里设置图片等内容 return cell }
然后一定要在FilterCollectionCell里重写prepareForReuse()方法,取消正在进行的图片下载任务并清空旧图——不然复用的时候,之前的下载任务还在后台跑,内存会越堆越多:
class FilterCollectionCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! override func prepareForReuse() { super.prepareForReuse() // 取消当前图片下载任务,释放资源 imageView.kf.cancelDownloadTask() // 清空旧图片,避免复用的时候显示错误内容+占用内存 imageView.image = nil } }
2. 给Kingfisher的图片加载「瘦个身」
默认的Kingfisher加载逻辑可能会直接下载原图,而CollectionViewCell的图片尺寸通常很小,这会导致内存里存了大量超大尺寸的图片,直接撑爆内存。
给图片做「降采样」(强制缩小到Cell需要的尺寸)
比如你的Cell里imageView是100x100的,就用DownsamplingImageProcessor让Kingfisher直接加载缩小后的图:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionCell", for: indexPath) as! FilterCollectionCell let imageUrl = yourDataSource[indexPath.item].imageUrl // 根据imageView的尺寸创建处理器 let downsampleProcessor = DownsamplingImageProcessor(size: cell.imageView.bounds.size) cell.imageView.kf.setImage( with: URL(string: imageUrl), placeholder: UIImage(named: "default_placeholder"), options: [ .processor(downsampleProcessor), .scaleFactor(UIScreen.main.scale), // 适配屏幕分辨率 .cacheOriginalImage // 可选:如果需要缓存原图的话可以保留,否则可以去掉 ] ) return cell }
限制Kingfisher的内存缓存大小
全局配置一下内存缓存的上限,避免它无限制缓存图片:
// 可以在App启动的时候(比如AppDelegate/SceneDelegate的didFinishLaunchingWithOptions里)配置 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 内存缓存上限设为100MB(根据你的APP需求调整) ImageCache.default.memoryStorage.config.totalCostLimit = 100 * 1024 * 1024 // 最多缓存200张图片(可选) ImageCache.default.memoryStorage.config.countLimit = 200 return true }
3. 优化CollectionView的渲染性能
如果Cell里有圆角、阴影这类耗时的渲染操作,会导致滚动卡顿,同时也会间接增加内存占用:
// 在Cell的awakeFromNib或者配置的时候开启光栅化 class FilterCollectionCell: UICollectionViewCell { override func awakeFromNib() { super.awakeFromNib() // 开启光栅化,把Cell的渲染结果缓存起来,滚动时不用重复渲染 layer.shouldRasterize = true layer.rasterizationScale = UIScreen.main.scale // 必须和屏幕分辨率一致,不然会模糊 } }
另外,确保你的CollectionView的布局是提前计算好的,比如用UICollectionViewFlowLayout的话,提前设置好itemSize,不要让系统在滚动时动态计算,这也会导致卡顿和额外的内存消耗。
4. 排查内存泄漏
用Xcode的Memory Graph Debugger(调试栏里的那个小方块图标)看看是不是有循环引用:
- 比如在Kingfisher的回调闭包里,如果引用了
self,一定要用weak self避免循环引用:cell.imageView.kf.setImage(with: URL(string: imageUrl)) { [weak self] result in guard let self = self else { return } // 这里处理图片加载完成后的逻辑,比如更新UI } - 另外,检查你的数据源模型——绝对不要在模型里存储UIImage对象,只存图片URL即可,让Kingfisher负责加载和缓存,不然数据源会占用大量内存。
按照上面的步骤一步步排查,大概率能解决你的滚动卡顿和内存崩溃问题!
内容的提问来源于stack exchange,提问作者Yasheed Mohammed

