多张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,请问代码中存在什么问题或遗漏点导致内存飙升?
你的代码里的“分批处理”逻辑完全没起到内存优化作用,反而可能额外增加开销,核心问题和解决办法如下:
核心问题
CGImageDestination的缓存机制
CGImageDestination会把所有添加的CGImage都缓存到内存中,直到调用CGImageDestinationFinalize才会写入文件并释放内存。不管分多少批,只要把所有帧都加进去,内存就会持续累积,直到处理完成。无效的分批逻辑
你创建的batchImages是原数组的切片拷贝,额外持有了一批UIImage实例,反而增加了内存占用,而且没有任何内存释放的逻辑。未优化图片尺寸
贴纸动画不需要高清分辨率,如果输入的UIImage尺寸过大(比如原图是屏幕几倍大),单帧内存占用就很高,20帧叠加后直接撑爆内存。
解决办法
用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 } } }缩小图片尺寸
提前将所有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前,先调用这个方法处理每一张图片。
移除不必要的数组拷贝
删除batchImages相关的切片逻辑,直接遍历原数组的元素,避免额外的内存占用。
内容的提问来源于stack exchange,提问作者Kodr.F

