collectionView单元格中的图像由于内存问题导致终止



我看了几个关于这个问题的答案,但都没有帮助。

vc1是一个常规的vc,我从firebase中获取20个图像,在vc2中,我使用ARKit在用户选择时显示这些图像。我在vc2中有一个collectionView,它对来自firebase的20多个图像进行分页。问题是,当接下来的20张图像加载完毕,我开始滚动时,应用程序会以Message from debugger: Terminated due to memory issue崩溃。当滚动这些新图像时,我看一下内存图,它会飙升到1吉,所以这就是崩溃的原因。ARKit和我浮动的节点也会导致内存的增加,但它们并不是崩溃的原因,如下所述。

1-在单元格中,我使用SDWebImage在imageView中显示图像。一旦我评论出SDWebImage,一切都正常,滚动很流畅,不会再崩溃,但我当然看不到图像。我切换到URLSession.shared.dataTask,同样的内存问题再次出现。

2-图像最初是用iPhone全屏摄像头拍摄的,并用jpegData(compressionQuality: 0.3).保存。手机尺寸为40x40。在SDWebImage完成块中,我试图调整图像的大小,但内存崩溃仍然存在。

3-我使用Instruments>Leaks查找内存泄漏,出现了一些Foundation泄漏,但当我关闭vc2时,Deinit总是运行。在vc2中没有任何长时间运行的定时器或循环,我在所有闭包中使用[weak self]

4-正如我在第二点中所说,问题肯定是imageView/image,因为一旦我将其从流程中删除,一切都会很好。如果我不显示任何图像,一切都很好(将显示40个红色图像视图,其中没有图像(。

我能做些什么来解决这个问题

分页以提取更多图像

for child in snapshot.children.allObjects as! [DataSnapshot] {
guard let dict = child.value as? [String:Any] else { continue }
let post = Post(dict: dict)
datasource.append(post)
let lastIndex = datasource.count - 1
let indexPath = IndexPath(item: lastIndex, section: 0)
UIView.performWithoutAnimation {
collectionView.insertItems(at: [indexPath])
}
}

cellForItem:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellId, for: indexPath) as! PostCell
cell.resetAll()
cell.post = dataSource[indexPath.item]
return cell
}

PostCell

private lazy var imageView: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.layer.cornerRadius = 15
iv.layer.masksToBounds = true
iv.backgroundColor = .red
iv.isHidden = true
return iv
}()
private lazy var spinner: UIActivityIndicatorView = {
let actIndi = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.medium) // ...
}()
var post: Post? {
didSet {
guard let urlSr = post?.urlStr, let url = URL(string: urlSr) else { return }
spinner.startAnimating()
// if I comment this out everything works fine
imageView.sd_setImage(with: url, placeholderImage: placeHolderImage, options: [], completed: { 
[weak self] (image, error, cache, url) in
// in here I tried resizing the image but no diff
DispatchQueue.main.async { [weak self] in
self?.showAll()
}
})
setAnchors() 
}
}
func resetAll() {
spinner.stopAnimating()
imageView.image = nil
imageView.removeFromSuperView()
imageView.isHidden = true
}
func showAll() {
spinner.stopAnimating()
imageView.isHidden = false
}
func setAnchors() {
contentView.addSubview(imageView)
imageView.addSubview(cellSpinner)
// imageView is pinned to all sides
}

在评论中@matt是100%正确的,问题是图像太大,不适合收藏尺寸为40x40的视图,我需要调整图像的大小。出于某种原因,我问题中的调整大小链接不起作用,所以我用它来调整图像的大小。我也使用URLSession和这个答案

我分10步完成,所有人都评论出

// 0. set this anywhere outside any class
let imageCache = NSCache<AnyObject, AnyObject>()

PostCell:

private lazy var imageView: UIImageView = { ... }()
private lazy var spinner: UIActivityIndicatorView = { ... }()
// 1. override prepareForReuse, cancel the task and set it to nil, and set the imageView.image to nil so that the wrong images don't appear while scrolling
override func prepareForReuse() {
super.prepareForReuse()

task?.cancel()
task = nil
imageView.image = nil
}
// 2. add a var to start/stop a urlSession when the cell isn't on screen
private var task: URLSessionDataTask?
var post: Post? {
didSet {
guard let urlStr = post?.urlStr else { return }
spinner.startAnimating()
// 3. crate the new image from this function
createImageFrom(urlStr)
setAnchors() 
}
}
func createImageFrom(_ urlStr: String) {

if let cachedImage = imageCache.object(forKey: urlStr as AnyObject) as? UIImage {

// 4. if the image is in cache call this function to show the image
showAllAndSetImageView(with: cachedImage)

} else {

guard let url = URL(string: urlStr) else { return }
// 5. if the image is not in cache start a urlSession and initialize it with the task variable from step 2
task = URLSession.shared.dataTask(with: url, completionHandler: {
[weak self](data, response, error) in

if let error = error { return }

if let response = response as? HTTPURLResponse {
print("response.statusCode: (response.statusCode)")
guard 200 ..< 300 ~= response.statusCode else { return }
}

guard let data = data, let image = UIImage(data: data) else { return }

// 6. add the image to cache
imageCache.setObject(image, forKey: photoUrlStr as AnyObject)

DispatchQueue.main.async { [weak self] in
// 7. call this function to show the image
self?.showAllAndSetImageView(with: image)
}

})
task?.resume()
}
}
func showAllAndSetImageView(with image: UIImage) {
// 8. resize the image
let resizedImage = resizeImageToCenter(image: image, size: 40) // can also use self.frame.height
imageView.image = resizeImage
showAll()
}
// 9. func to resize the image
func resizeImageToCenter(image: UIImage, size: CGFloat) -> UIImage {
let size = CGSize(width: size, height: size)
// Define rect for thumbnail
let scale = max(size.width/image.size.width, size.height/image.size.height)
let width = image.size.width * scale
let height = image.size.height * scale
let x = (size.width - width) / CGFloat(2)
let y = (size.height - height) / CGFloat(2)
let thumbnailRect = CGRect.init(x: x, y: y, width: width, height: height)
// Generate thumbnail from image
UIGraphicsBeginImageContextWithOptions(size, false, 0)
image.draw(in: thumbnailRect)
let thumbnail = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return thumbnail!
}
func resetAll() { ... }
func showAll() { ... }
func setAnchors() { ... }

相关内容

  • 没有找到相关文章

最新更新