使用PHPickerViewController选图预览时内存占用极高如何解决?
内存暴涨根本原因
你直接将全尺寸原始图片赋值给UI组件时,系统会将图片完整解码为位图存到内存,内存占用仅和图片像素数正相关,和图片的磁盘压缩大小(比如jpg/png的文件大小)无关。位图内存占用计算公式为:图片宽度像素 * 图片高度像素 * 4字节 / 1024 / 1024,比如一张12000px * 10000px的原图,解码后内存占用刚好接近480MB,和你观测的涨幅一致。UIImageView的显示尺寸不会影响解码后的内存占用,就算你用100pt*100pt的视图显示大尺寸原图,还是会占用全尺寸位图的内存。
最优修复方案
- 优先对图片做下采样后再赋值给预览视图,只加载和预览视图尺寸匹配的像素到内存,不要直接加载全尺寸图片
- 不要使用
itemProvider.loadObject(ofClass: UIImage.self)读取PHPicker返回的结果,这个接口会默认加载全尺寸图到内存,改用ImageIO框架做目标尺寸下采样,效率更高内存占用更低
通用下采样工具方法
func downsampleImage(at url: URL, to targetSize: CGSize, scale: CGFloat = UIScreen.main.scale) -> UIImage? { let maxDimension = max(targetSize.width, targetSize.height) * scale let options: [CFString: Any] = [ kCGImageSourceCreateThumbnailFromImageAlways: true, kCGImageSourceThumbnailMaxPixelSize: maxDimension, kCGImageSourceCreateThumbnailWithTransform: true ] guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil), let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil } return UIImage(cgImage: cgImage, scale: scale, orientation: .up) }
PHPicker回调正确写法示例
import UniformTypeIdentifiers // UTType需要依赖该框架 func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) // 替换为你实际的预览视图尺寸 let previewTargetSize = CGSize(width: 300, height: 300) guard let result = results.first else { return } // 先获取图片文件路径,避免全尺寸数据加载到内存 result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.image.identifier) { [weak self] url, error in guard let url = url, error == nil else { return } // 拷贝到自有沙盒目录,避免系统自动删除临时文件 let tempDir = FileManager.default.temporaryDirectory let targetURL = tempDir.appendingPathComponent(UUID().uuidString + url.pathExtension) do { try FileManager.default.copyItem(at: url, to: targetURL) // 后台线程执行下采样,避免卡主线程 DispatchQueue.global().async { let previewImage = downsampleImage(at: targetURL, to: previewTargetSize) DispatchQueue.main.async { // 主线程赋值给预览视图 self?.previewImageView.image = previewImage // 后续需要上传原图时直接读取targetURL路径即可,无需持有全尺寸UIImage } } } catch { // 自定义处理文件拷贝错误逻辑 } } }
额外优化建议
- 如果是多选图片预览,采用懒加载机制,仅加载当前屏幕可见的图片预览,滑出屏幕的视图及时置空image属性释放内存
- 上传原图时直接读取沙盒里存储的原图片文件路径即可,全程不需要持有全尺寸UIImage对象
内容的提问来源于stack exchange,提问作者OPS Klings
相关产品推荐
相关产品推荐

