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

多张UIImage合成单张GIF时内存泄漏致应用崩溃问题排查

问题

尝试将多张UIImage合成一张GIF动图,当帧数(UIImage数量)超过20时,应用会因内存泄漏崩溃。调用createGIF方法生成GIF时,内存从200MB飙升至2GB并持续增长直至崩溃。以下是相关代码:

func createGIF(from images: [UIImage], loopCount: Int = 0, frameDuration: TimeInterval = 0.1, completion: @escaping (URL?) -> Void) {
    guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
        print("Error: Unable to create documentsDirectory.")
        completion(nil)
        return
    }

    let gifURL = documentsDirectory.appendingPathComponent("\(UUID().uuidString).gif")

    guard let destination = CGImageDestinationCreateWithURL(gifURL as CFURL, kUTTypeGIF, images.count, nil) else {
        print("Error: Unable to create CGImageDestination.")
        completion(nil)
        return
    }

    let gifProperties = [
        kCGImagePropertyGIFDictionary as String: [
                    kCGImagePropertyGIFLoopCount as String: loopCount
                ]
    ]
    CGImageDestinationSetProperties(destination, gifProperties as CFDictionary)

    // Process frames in batches to reduce memory usage
    let batchSize = 10
    let totalFrames = images.count

    for startIndex in stride(from: 0, to: totalFrames, by: batchSize) {
        let endIndex = min(startIndex + batchSize, totalFrames)
        let batchImages = Array(images[startIndex..<endIndex])

        for image in batchImages {
            if let cgImage = image.cgImage {
                let frameProperties = [
                    kCGImagePropertyGIFDictionary as String: [
                        kCGImagePropertyGIFDelayTime as String: frameDuration
                    ]
                ]
                CGImageDestinationAddImage(destination, cgImage, frameProperties as CFDictionary)
            } else {
                print("Error: Unable to retrieve CGImage from UIImage.")
                completion(nil)
                return
            }
        }
    }

    guard CGImageDestinationFinalize(destination) else {
        print("Error: Failed to finalize CGImageDestination.")
        completion(nil)
        return
    }

    completion(gifURL)
}

想实现贴纸动画GIF,请问代码中存在什么问题或遗漏点导致内存飙升?

分析与解决方案

你的代码里的“分批处理”逻辑完全没起到内存优化作用,反而可能额外增加开销,核心问题和解决办法如下:

核心问题

  1. CGImageDestination的缓存机制
    CGImageDestination会把所有添加的CGImage都缓存到内存中,直到调用CGImageDestinationFinalize才会写入文件并释放内存。不管分多少批,只要把所有帧都加进去,内存就会持续累积,直到处理完成。

  2. 无效的分批逻辑
    你创建的batchImages是原数组的切片拷贝,额外持有了一批UIImage实例,反而增加了内存占用,而且没有任何内存释放的逻辑。

  3. 未优化图片尺寸
    贴纸动画不需要高清分辨率,如果输入的UIImage尺寸过大(比如原图是屏幕几倍大),单帧内存占用就很高,20帧叠加后直接撑爆内存。

解决办法

  1. 用autoreleasepool及时释放单帧内存
    去掉多余的分批逻辑,直接遍历原数组,用autoreleasepool包裹每帧的处理,让系统及时回收单帧的CGImage和UIImage内存:

    for image in images {
        autoreleasepool {
            if let cgImage = image.cgImage {
                let frameProperties = [
                    kCGImagePropertyGIFDictionary as String: [
                        kCGImagePropertyGIFDelayTime as String: frameDuration
                    ]
                ]
                CGImageDestinationAddImage(destination, cgImage, frameProperties as CFDictionary)
            } else {
                print("Error: Unable to retrieve CGImage from UIImage.")
                completion(nil)
                return
            }
        }
    }
    
  2. 缩小图片尺寸
    提前将所有UIImage缩到贴纸需要的合适大小(比如300x300),大幅降低单帧内存占用:

    func resizeImage(_ image: UIImage, targetSize: CGSize) -> UIImage? {
        let size = image.size
        let scaleRatio = min(targetSize.width/size.width, targetSize.height/size.height)
        let newSize = CGSize(width: size.width*scaleRatio, height: size.height*scaleRatio)
        UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
        image.draw(in: CGRect(origin: .zero, size: newSize))
        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resizedImage
    }
    

    在添加到CGImageDestination前,先调用这个方法处理每一张图片。

  3. 移除不必要的数组拷贝
    删除batchImages相关的切片逻辑,直接遍历原数组的元素,避免额外的内存占用。

内容的提问来源于stack exchange,提问作者Kodr.F

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 17:05:29