来自调试器的消息:由于内存问题而终止使用 gif 图像 Kingfisher Library 在 UITableView/UICollectionView 中滚动时

Message from debugger: Terminated due to memory issue While scrolling in UITableView/UICollectionView with gif images Kingfisher Library

代码片段:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: BroadCastFeedTableViewCell = broadCastTableView.dequeueReusableCell(withIdentifier: "broadCastFeedCell") as BroadCastFeedTableViewCell

    cell.singlePicImageView.kf.setImage(with: URL(string: (self.broadCastArray[indexPath.row].images_url?[0])!), placeholder: nil, options: nil, progressBlock: nil) 
            { (theImage, error, cache, url) in
                if theImage != nil{
                    let imageHeight = self.getAspectRatioOfDownloadedImages(resolution: self.broadCastArray[indexPath.row].image_resolution![0])
                    cell.collectionContentViewHeightConstraint.constant = imageHeight

                    cell.layoutSubviews()
                    cell.layoutIfNeeded()

                }else {
                    let placeHolderCenterCordi = UIView().getViewRelationWithSuperSuperView(viewControllerView: cell.collectionContentView, 
                                                                    subView: cell.singlePicImageView, subObjFrame: cell.singlePicImageView.frame)

                    cell.singlePicImageView.addPlaceHolderImageView(cordinates: placeHolderCenterCordi.center)
                }
               self.broadCastTableView.rowHeight = UITableViewAutomaticDimension
            }
}

在上面的代码中,我使用了 Kingfisher 库从远程服务器加载图像,每当以下代码加载某些可能具有较大尺寸(大约 2-5 MB)的图像(GIF、JPEG、PNG)时,应用程序会由于以下原因而终止内存问题。在 iPhone 5s 中,它会在应用程序启动后立即终止,而在其他 iPhone 模型 (7, 6s) 中,它会在滚动一定时间后终止。我还检查了 Allocation 和 leak,但我没有 understand/found 了解这个问题。

我还附上了分析图。这表明不存在此类内存泄漏,但由于某些问题应用正在终止:

我认为在您的代码中您正在使用 "Self" 作为强烈的克制,因此它正在创建一个强大的引用循环,因为这会造成内存泄漏。所以此时你可以尝试使用 [weak self] 而不是 运行 你的代码

cell.singlePicImageView.kf.setImage(with: URL(string: (self.broadCastArray[indexPath.row].images_url?[0])!), placeholder: nil, options: nil, progressBlock: nil) 
        { [weak self] (theImage, error, cache, url) in
            if theImage != nil{
                let imageHeight = self?.getAspectRatioOfDownloadedImages(resolution: self?.broadCastArray[indexPath.row].image_resolution![0])
                cell.collectionContentViewHeightConstraint.constant = imageHeight

                cell.layoutSubviews()
                cell.layoutIfNeeded()

            }else {
                let placeHolderCenterCordi = UIView().getViewRelationWithSuperSuperView(viewControllerView: cell.collectionContentView, 
                                                                subView: cell.singlePicImageView, subObjFrame: cell.singlePicImageView.frame)

                cell.singlePicImageView.addPlaceHolderImageView(cordinates: placeHolderCenterCordi.center)
            }
           self?.broadCastTableView.rowHeight = UITableViewAutomaticDimension
        }

我在加载图像时遇到了很多问题。我不能确定你的情况,但我可以给你一些有用的提示。

  1. 查看 UITableViewDataSourcePrefetching。这是一种在单元格中呈现图像之前下载图像的方法。
  2. 考虑在某处创建一个 Helper 函数来启动下载并允许取消该下载。例如,当你快速向下滚动一个表格视图时,每个单元格都会开始下载图像,即使它没有显示。如果单元格消失,您可以取消下载。
  3. 单元格基本上应该是代码中的 UI 元素。这个想法是,尝试从单元本身中取出网络请求,并确保您的单元仅包含要呈现的元素(图像、标签、视图),单元具有 func configure(withModel: Model) 函数,并且在 cellForRow 上,您只需将数据传递给它进行渲染。单元格被重复使用并经常更新,你不应该在上面执行一堆东西。请记住,当一个单元格被加载时,它会调用这些函数,然后它可以离开屏幕并再次返回,它会再次重新执行所有这些。
  4. 您的单元格 class 是否执行了正确的布局更新和刷新,并尽量不要在 cellForRow 中执行此操作。您所能做的就是确保 imageview 获取图像,但不依赖于执行单元格更改,并且此图像下载是异步的,下载结束时该单元格甚至可能不存在。
  5. 缓存。您可以缓存这些图像吗?它们会不断变化吗?确保您了解 KF 如何缓存您的图像以及它正在执行您需要的缓存。如果没有,适应。
  6. [吹毛求疵]你打电话给 cell.layoutSubviews() 然后 cell.layoutIfNeeded()。第一个是强制调用 layoutSubviews,第二个是在调用 layoutSubView 之前检查是否有需要布局的东西。如果您想标记要布局的视图,您可以执行 setNeedsLayout()。这将使视图布局在下一个周期更新。

回答我自己的问题

以下对我有用

我使用了 AnimatedImageView class 作为 kingfisher 库提供的 UIImageView

let imageView = AnimatedImageView()
imageView.kf.setImage(with: URL(string: "your_image"), placeholder: nil, options: [.targetCache(ImageCache(name: "your_cache_name")), .backgroundDecode], progressBlock: nil) { (image, error, cache, url) in 
  // some code
}

注意重要

[.targetCache(ImageCache(name: "your_cache_name")), .backgroundDecode]

在上面 options 属性的代码中,我使用了自己的缓存 (ImageCache(name: "your_cache_name")) 并询问kingfisher 在后台解码图像