Swift中传递CVPixelBuffer给CoreImage/UIKit后如何释放?
内存泄漏问题:24位BMP转JPEG时的内存管理异常
我用CVPixelBufferCreateWithBytes(_:_:_:_:_:_:_:_:_:_:)从磁盘读取24位BMP,代码如下:
var data = try! Data(contentsOf: url.appendingPathExtension(for: .bmp)) data.withUnsafeMutableBytes { (unsafeMutableRawBufferPointer: UnsafeMutableRawBufferPointer) in var pixelBuffer: CVPixelBuffer? CVPixelBufferCreateWithBytes( kCFAllocatorDefault, imageWidth, imageHeight, kCVPixelFormatType_24RGB, unsafeMutableRawBufferPointer.baseAddress!, bytesPerRow, nil, nil, nil, &pixelBuffer ) }
尝试将其转换为JPEG时,所有相关方法都存在内存泄漏,例如:
try! UIImage(ciImage: CIImage(cvPixelBuffer: pixelBuffer!)) .jpegData(compressionQuality: 0.0)! .write(to: someUrl)
以及Core Image的写入方法:
try! CIContext().writeJPEGRepresentation( of: CIImage(cvPixelBuffer: pixelBuffer!), to: someUrl, colorSpace: CGColorSpaceCreateDeviceRGB() )
使用autoreleasepool包裹代码也无法解决泄漏问题:
autoreleasepool { var pixelBuffer: CVPixelBuffer? CVPixelBufferCreateWithBytes( kCFAllocatorDefault, imageWidth, imageHeight, kCVPixelFormatType_24RGB, unsafeMutableRawBufferPointer.baseAddress!, bytesPerRow, nil, nil, nil, &pixelBuffer ) try! UIImage(ciImage: CIImage(cvPixelBuffer: pixelBuffer!)) .jpegData(compressionQuality: 0.0)! .write(to: someUrl) }
同时,所有*Release方法均不可用,编译器报错:'CFRelease' is unavailable: Core Foundation objects are automatically memory managed。
进一步测试发现,泄漏仅发生在调用JPEG相关方法时:
- 存在泄漏的代码:
let uiImage = UIImage(ciImage: CIImage(cvPixelBuffer: pixelBuffer!)) .jpegData(compressionQuality: 0.0)!
- 无泄漏的代码:
let uiImage = UIImage(ciImage: CIImage(cvPixelBuffer: pixelBuffer!))
Core Image也遵循同样的规律:
- 无泄漏的代码:
CIContext().createCGImage(CIImage(cvPixelBuffer: pixelBuffer!), from: CGRect(x: 0, y: 0, width: imageWidth, height: imageHeight))
- 存在泄漏的代码:
CIContext().writeJPEGRepresentation( of: CIImage(cvPixelBuffer: pixelBuffer!), to: someUrl, colorSpace: CGColorSpaceCreateDeviceRGB() )
我猜测需要通过向CVPixelBufferCreateWithBytes传递CVPixelBufferReleaseBytesCallback来管理内存,但不清楚正确的用法。多数示例给出的回调写法如下:
let releaseCallBack: CVPixelBufferReleaseBytesCallback = { _, pointer in if let pointer = pointer { free(UnsafeMutableRawPointer(mutating: pointer)) } }
但使用该回调会导致autoreleasepool执行时抛出异常,原因是回调试图释放已不存在的内存;如果移除该回调,又会导致对象持续泄漏。手动调用指针的deallocate方法也无法解决问题。
内容的提问来源于stack exchange,提问作者Tae
相关产品推荐
相关产品推荐

