You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS应用从PHAssets生成图片时因内存问题崩溃求助

解决PHAsset获取原图导致内存崩溃的问题

崩溃原因

  • 手机相册中的照片多为高分辨率(比如4000×3000甚至更高),直接加载原始像素尺寸的图片会占用大量内存,批量加载时会瞬间耗尽应用可用内存,触发崩溃。
  • 同步请求(option.isSynchronous = true)会强制系统立即加载全尺寸图片,缺乏内存缓冲优化,进一步加剧内存压力。

解决方案

1. 按需加载适配尺寸的图片

不要直接使用self.pixelWidth/self.pixelHeight作为目标尺寸,根据实际显示场景设置合理大小,比如屏幕分辨率、控件显示尺寸,既满足需求又控制内存占用。

2. 改用异步请求加载图片

同步请求会阻塞线程且内存管理效率低,异步请求配合回调处理图片,系统会自动优化内存分配,避免批量加载导致的内存暴涨。

3. 优化批量加载逻辑

不要一次性将所有图片存入数组,建议在列表(UITableView/UICollectionView)滚动时,仅加载可见区域的图片;也可以使用PHCachingImageManager实现图片预加载与缓存,由系统自动管理内存。

修改后的代码示例

异步获取图片的PHAsset扩展

extension PHAsset {
    func getImage(targetSize: CGSize, completion: @escaping (UIImage?) -> Void) {
        let manager = PHImageManager.default()
        let options = PHImageRequestOptions()
        // 按需选择交付模式:追求速度用.fastFormat,追求质量用.highQualityFormat
        options.deliveryMode = .highQualityFormat
        // 允许访问iCloud中的图片(如果需要)
        options.isNetworkAccessAllowed = true
        
        manager.requestImage(for: self,
                             targetSize: targetSize,
                             contentMode: .aspectFit,
                             options: options) { result, info in
            completion(result)
        }
    }
}

异步批量加载相册截图

func fetchPhotos() {
    let fetchOptions = PHFetchOptions()
    fetchOptions.predicate = NSPredicate(format: "(mediaSubtype & %d) != 0", PHAssetMediaSubtype.photoScreenshot.rawValue)
    
    let fetchedResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
    
    // 设置目标尺寸为屏幕分辨率大小(适配显示需求)
    let screenScale = UIScreen.main.scale
    let targetSize = CGSize(width: UIScreen.main.bounds.width * screenScale, 
                            height: UIScreen.main.bounds.height * screenScale)
    
    for index in 0..<fetchedResult.count {
        let asset = fetchedResult.object(at: index)
        asset.getImage(targetSize: targetSize) { [weak self] image in
            guard let self = self, let image = image else { return }
            self.testImages.append(image)
            // 主线程更新UI(比如刷新列表)
            DispatchQueue.main.async {
                // self.yourCollectionView.reloadData()
            }
        }
    }
}

额外注意事项

  • 不要强制解包result!,请求可能因网络、权限等原因失败,必须做可选绑定处理。
  • 使用[weak self]避免闭包导致的循环引用,防止内存泄漏。
  • 如果确实需要加载原图,务必分批加载,并及时释放不再使用的图片对象,避免内存累积。

内容的提问来源于stack exchange,提问作者narner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:35:22