来自调试器的消息:由于内存问题而终止使用 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
}
我在加载图像时遇到了很多问题。我不能确定你的情况,但我可以给你一些有用的提示。
- 查看 UITableViewDataSourcePrefetching。这是一种在单元格中呈现图像之前下载图像的方法。
- 考虑在某处创建一个 Helper 函数来启动下载并允许取消该下载。例如,当你快速向下滚动一个表格视图时,每个单元格都会开始下载图像,即使它没有显示。如果单元格消失,您可以取消下载。
- 单元格基本上应该是代码中的 UI 元素。这个想法是,尝试从单元本身中取出网络请求,并确保您的单元仅包含要呈现的元素(图像、标签、视图),单元具有 func configure(withModel: Model) 函数,并且在 cellForRow 上,您只需将数据传递给它进行渲染。单元格被重复使用并经常更新,你不应该在上面执行一堆东西。请记住,当一个单元格被加载时,它会调用这些函数,然后它可以离开屏幕并再次返回,它会再次重新执行所有这些。
- 您的单元格 class 是否执行了正确的布局更新和刷新,并尽量不要在 cellForRow 中执行此操作。您所能做的就是确保 imageview 获取图像,但不依赖于执行单元格更改,并且此图像下载是异步的,下载结束时该单元格甚至可能不存在。
- 缓存。您可以缓存这些图像吗?它们会不断变化吗?确保您了解 KF 如何缓存您的图像以及它正在执行您需要的缓存。如果没有,适应。
- [吹毛求疵]你打电话给
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 在后台解码图像
代码片段:
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
}
我在加载图像时遇到了很多问题。我不能确定你的情况,但我可以给你一些有用的提示。
- 查看 UITableViewDataSourcePrefetching。这是一种在单元格中呈现图像之前下载图像的方法。
- 考虑在某处创建一个 Helper 函数来启动下载并允许取消该下载。例如,当你快速向下滚动一个表格视图时,每个单元格都会开始下载图像,即使它没有显示。如果单元格消失,您可以取消下载。
- 单元格基本上应该是代码中的 UI 元素。这个想法是,尝试从单元本身中取出网络请求,并确保您的单元仅包含要呈现的元素(图像、标签、视图),单元具有 func configure(withModel: Model) 函数,并且在 cellForRow 上,您只需将数据传递给它进行渲染。单元格被重复使用并经常更新,你不应该在上面执行一堆东西。请记住,当一个单元格被加载时,它会调用这些函数,然后它可以离开屏幕并再次返回,它会再次重新执行所有这些。
- 您的单元格 class 是否执行了正确的布局更新和刷新,并尽量不要在 cellForRow 中执行此操作。您所能做的就是确保 imageview 获取图像,但不依赖于执行单元格更改,并且此图像下载是异步的,下载结束时该单元格甚至可能不存在。
- 缓存。您可以缓存这些图像吗?它们会不断变化吗?确保您了解 KF 如何缓存您的图像以及它正在执行您需要的缓存。如果没有,适应。
- [吹毛求疵]你打电话给
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 在后台解码图像