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

将UIView转为图片时内存飙升致APP崩溃的问题求助

横向长图分割预览时内存溢出崩溃的解决方案

我的ScrollView内有一个横向UIView,包含多张大尺寸图片,需将其分割为轮播帖规格的预览图,但执行生成预览的函数时APP因内存不足崩溃,排查确认是渲染预览阶段内存占用过高。

原实现代码

生成预览图函数

func generatePreviewImages() -> [UIImage] {
    let arrMaskView = properties.arrAllObjectView.filter({ ($0 as? MaskView) != nil }) as? [MaskView] ?? [MaskView]()
    
    arrMaskView.forEach({ $0.imageViewAddIcon.isHidden = true })
    
    var arrImage = [UIImage]()
    for i in 0..<Int(properties.cols) {
        autoreleasepool(invoking: {
            let x = (viewCanvas.frame.size.width / properties.cols.cgFloat) * CGFloat(i)
            let width = (viewCanvas.frame.size.width / properties.cols.cgFloat)
            if let image = viewCanvas.asImage(bounds: CGRect.init(x: x, y: 0, width: width, height: viewCanvas.bounds.height)) {
                arrImage.append(image)
            }
        })
    }
    
    arrMaskView.forEach({ $0.imageViewAddIcon.isHidden = !($0.imageUser == nil) })
    
    return arrImage
}

UIView转图片函数

func asImage(bounds:CGRect) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(bounds: bounds)
    return renderer.image { rendererContext in
        layer.render(in: rendererContext.cgContext)
    }
}

优化方案

1. 跳过UIView渲染,直接裁剪原始图片

原方案每次分割都渲染整个大View,会重复加载全量大图到内存。若能直接获取原始图片资源,优先使用UIImage原生裁剪API,避免UI渲染开销:

// 从原始大图裁剪指定区域(适配UIImage坐标系)
func cropImage(from originalImage: UIImage, to targetRect: CGRect) -> UIImage? {
    guard let cgImage = originalImage.cgImage else { return nil }
    let scale = originalImage.scale
    // 转换UIKit坐标系到CGImage坐标系,并适配缩放因子
    let cropRect = CGRect(
        x: targetRect.origin.x * scale,
        y: (originalImage.size.height - targetRect.origin.y - targetRect.height) * scale,
        width: targetRect.width * scale,
        height: targetRect.height * scale
    )
    guard let croppedCGImage = cgImage.cropping(to: cropRect) else { return nil }
    return UIImage(cgImage: croppedCGImage, scale: scale, orientation: originalImage.imageOrientation)
}

2. 优化UIView渲染逻辑

若必须从UIView生成图片,需避免渲染整个View,只渲染目标区域,并降低预览图分辨率:

// 优化后的UIView转图片函数,支持指定缩放因子和裁剪区域
func asImage(bounds: CGRect, scale: CGFloat = UIScreen.main.scale * 0.5) -> UIImage? {
    let format = UIGraphicsImageRendererFormat()
    format.scale = scale // 降低缩放因子减少像素量,根据预览需求调整
    let renderer = UIGraphicsImageRenderer(bounds: bounds, format: format)
    
    return renderer.image { context in
        context.saveGState()
        // 只裁剪并渲染目标区域
        context.clip(to: bounds)
        context.translateBy(x: -bounds.origin.x, y: -bounds.origin.y)
        layer.render(in: context.cgContext)
        context.restoreGState()
    }
}

3. 按需生成预览图

不要一次性生成所有预览图,而是结合轮播控件的滚动事件,按需生成并缓存已生成的预览图,避免同时持有大量图片实例。

4. 强化内存管理

  • 确保生成的预览图为CGImage-backed类型,避免CIImage带来的额外内存开销
  • 生成预览后及时释放原始大图的强引用
  • 保留autoreleasepool包裹单张图片的生成逻辑,确保临时对象及时回收

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 18:15:01