如何使用更少的内存在 collectionView 中显示照片库

Posted

技术标签:

【中文标题】如何使用更少的内存在 collectionView 中显示照片库【英文标题】:How to display photo library in collectionView using less memory 【发布时间】:2018-10-20 20:02:05 【问题描述】:

我面临的问题是人们说在 collectionView 中加载照片库时应用程序崩溃。大多数时候,他们有超过 2-3 千张照片。对我来说它工作正常。 (我的图库中只有不到 1000 张照片)。

问题:也许有什么方法可以在 collectionView 中显示图像更“智能”并使用更少的内存?

P.S 也许当用户在 iCloud 中拥有他的所有照片时也会导致崩溃?因为直到现在我认为应用程序在将其加载到单元格时并未下载照片。也许有人可以证明或不同意这一事实。

这是我的功能:

func grabPhotos()

    let imgManager = PHImageManager.default()

    let requestOptions = PHImageRequestOptions()
    requestOptions.isSynchronous = false
    requestOptions.deliveryMode = .opportunistic // Quality of images
    requestOptions.isNetworkAccessAllowed = true

    requestOptions.isSynchronous = true
    requestOptions.progressHandler =   (progress, error, stop, info) in
        print("progress: \(progress)")
    

    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

    if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) 

        if fetchResult.count > 0 
            for i in 0..<fetchResult.count 
                imgManager.requestImage(for: fetchResult.object(at: i) , targetSize: CGSize(width: 150, height: 150), contentMode: .aspectFill, options: requestOptions, resultHandler:  image, error in
                    self.imageArray.append(image!)
                    self.kolekcija.reloadData()
                )
            
        
        else 
            print("You got no photos")
            kolekcija.reloadData()
        
    


【问题讨论】:

这当然不是答案,但我相信 UICollectionView 的工作方式与 UITableView 完全相同 - 也就是说,您不是与数千人一起工作的细胞。这些控件被构建为通过对它们的单元进行排队来尽可能少地使用内存。我想知道你的问题是否出在其他地方。也许你是如何退休/存储你的数据源的? 虽然与问题无关,但每次在self.imageArray 中添加新项目时停止重新加载集合视图。只需在循环完成时重新加载集合视图。它不会对实时图像显示做任何事情,因为主队列正忙于准备图像数组。 【参考方案1】:

问题出在这一行:

self.imageArray.append(image!)

切勿制作图像数组。图片很大,你会用完内存。

这是一个集合视图。您只需在 itemForRowAt: 中按需提供图像,您提供的是图像的 small 版本(即所谓的 thumbnail),对于显示屏尺寸。因此,任何时候都只有足够的缩略图来填满可视屏幕;这不会占用太多内存,而且运行时可以在图像滚出屏幕时释放图像内存。

这就是 PHCachingImageManager 的用途。仔细看看 Apple 在他们的示例代码中是如何处理这个问题的:

https://developer.apple.com/library/archive/samplecode/UsingPhotosFramework/Listings/Shared_AssetGridViewController_swift.html

在该示例中任何地方都没有图像数组。

【讨论】:

这是一个很好的解决方案。当大约有 1000 张照片时,从 80 mb 下降到 10 mb :) 无论如何,我不知道这可能是我的错,也可能不是,但是使用这段代码,所选择的图像的质量并不是那么好。 (没有太大区别,但仍然)。无论如何,谢谢! @matt 很棒的参考伙伴。尽管当我高速向下滚动图像的 collectionView 网格或滚动大量图像(比如 700)时,在某些时候应用程序由于内存问题而终止。对此有什么想法吗? T.I.A @HudiIlfeld 听起来您正在保留收到的图像。不要那样做。正如我在回答中所说,您应该保留的唯一图像是在任何给定时刻实际显示在集合视图中的缩略图(小)图像。 Apple 提供了出色的 WWDC 视频,介绍如何确保不会缓存不需要缓存的图像。【参考方案2】:

在 Swift 中工作 5. 获取照片资源,但在 cellForItem 迭代之前不要加载照片。编码愉快。

    var personalImages: PHFetchResult<AnyObject>!
            
    func getPersonalPhotos() 
         let fetchOptions = PHFetchOptions()
         fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
         self.personalImages = (PHAsset.fetchAssets(with: .image, options: fetchOptions) as AnyObject?) as! PHFetchResult<AnyObject>? 
            
         photoLibraryCollectionView.reloadData()
    
        
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        
         return personalImages.count
    

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "insertCellIdentifierHere", for: indexPath) as! InsertACollectionViewCellHere
    
    let asset: PHAsset = self.personalImages[indexPath.item] as! PHAsset
    let itemWidth = photoLibraryCollectionView.frame.size.width / 2
    let itemSize = CGSize(width: itemWidth, height: itemWidth / 2) // adjust to your preferences
    
    PHImageManager.default().requestImage(for: asset, targetSize: itemSize, contentMode: .aspectFill, options: nil, resultHandler: (result, info)in
        if result != nil 
            
            cell.photoImageView.image = result
        
    )
    
    cell.layoutIfNeeded()
    cell.updateConstraintsIfNeeded()
    return cell

【讨论】:

以上是关于如何使用更少的内存在 collectionView 中显示照片库的主要内容,如果未能解决你的问题,请参考以下文章

如何让 MySQL 使用更少的内存?

尝试使用 itertools.permutations 时出现 MemoryError,如何使用更少的内存?

为啥在这段代码中向量比指针使用更少的内存?

调用垃圾回收会导致程序在java中使用更少的堆内存

哪个向量和地图,使用更少的内存(大量数据和未知大小)

为啥尽管我在变量中使用 malloc 分配更多内存,但当我打印变量的大小时,它仍然显示更少的内存/字节? [复制]