如何避免图片缩放/HEIC解码过程中的‘内存不足’崩溃?
高内存图片处理优化方案(iOS < 11 HEIC兼容场景)
针对大尺寸HEIC图片解码导致的OOM崩溃问题,可从提前内存预估、动态解码控制、参数优化三个维度解决:
1. 提前预估内存占用,规避风险
在解码前先获取HEIC图片的原始尺寸,计算解码后所需的内存空间,结合设备剩余可用内存判断是否执行解码操作:
- 单像素内存计算:默认RGBA格式为4字节/像素,改用RGB格式可降至3字节/像素
- 设备可用内存可通过系统API获取,设置安全阈值(比如保留至少50MB可用内存)
示例代码:获取HEIC原始尺寸与内存校验
func getHEICOriginalSize(from data: Data) -> CGSize? { guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil) else { return nil } let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any] let width = properties?[kCGImagePropertyPixelWidth] as? CGFloat ?? 0 let height = properties?[kCGImagePropertyPixelHeight] as? CGFloat ?? 0 return CGSize(width: width, height: height) } func canProcessImage(with size: CGSize, pixelBytes: Int = 4) -> Bool { let requiredMemory = size.width * size.height * CGFloat(pixelBytes) let requiredMB = requiredMemory / (1024 * 1024) let freeMemory = ProcessInfo.processInfo.physicalMemory - ProcessInfo.processInfo.usedMemory let freeMB = freeMemory / (1024 * 1024) // 预留安全内存,避免触发系统内存警告 return requiredMB < (freeMB - 50) }
2. 解码过程中监控内存,触发阈值则终止
在解码前后加入内存检查,若内存占用超过设定阈值(比如物理内存的80%),则停止操作并释放资源:
示例代码:带内存监控的解码逻辑
func resizePicture() -> UIImage? { let maxResolution = Int( Float(Swift.max(UIScreen.main.bounds.width, UIScreen.main.bounds.height)) * Float(UIScreen.main.scale) ) guard #available(iOS 11.0, *), false else { print("♻️ HEIF Decoding using SDWebImageHEIFCoder") let heifCoder = SDImageHEIFCoder.shared SDImageCodersManager.shared.addCoder(heifCoder) guard let heifData = try? Data(contentsOf: self.localURL) else { return nil } // 1. 提前校验图片尺寸与内存 guard let originalSize = getHEICOriginalSize(from: heifData), canProcessImage(with: originalSize) else { print("⚠️ 图片过大,内存不足,终止解码") return nil } // 2. 按比例缩小目标尺寸,避免不必要的内存消耗 let targetSize = CGSize(width: maxResolution, height: maxResolution) let scaledSize = scaleSize(originalSize, toFit: targetSize) // 3. 解码前再次检查内存 guard isMemoryAvailable() else { return nil } let image = SDImageHEIFCoder.shared.decodedImage(with: heifData, options: [ .decodeThumbnailPixelSize: scaledSize, .decodeFormat: SDImageFormat.RGB.rawValue // 改用RGB格式减少单像素内存 ]) // 解码后内存过高则释放图片 if !isMemoryAvailable() { return nil } return image } // iOS 11+ 系统原生处理逻辑(保留原有代码) return nil } // 辅助函数:按比例缩小尺寸 func scaleSize(_ original: CGSize, toFit target: CGSize) -> CGSize { let widthRatio = target.width / original.width let heightRatio = target.height / original.height let scale = min(widthRatio, heightRatio) return CGSize(width: original.width * scale, height: original.height * scale) } // 辅助函数:检查内存占用是否在安全范围内 func isMemoryAvailable() -> Bool { let usedRatio = Double(ProcessInfo.processInfo.usedMemory) / Double(ProcessInfo.processInfo.physicalMemory) return usedRatio < 0.8 }
3. 其他优化细节
- 避免一次性加载完整HEIC数据:改用
CGImageSource分块读取,替代Data(contentsOf:)加载全部数据 - 解码后及时释放资源:主动清空
heifData等临时变量,减少内存持有 - 后台线程处理:将解码操作放在全局并发队列,避免阻塞主线程同时分散内存压力
内容的提问来源于stack exchange,提问作者Pix
相关产品推荐
相关产品推荐

