Swift中如何手动释放CMSampleBuffer?解决内存泄漏崩溃问题
解决Swift中CMSampleBuffer内存泄漏问题
嘿,我正好碰到过类似的坑,咱们一步步来把它搞定!
首先看你代码里的核心问题:你调用了CMSampleBufferInvalidate(sampleBuffer),但这个方法只是标记缓冲区无效,并不会释放它占用的内存。而且你把它放在withUnsafeMutableBytes闭包里面,这时候你还在使用从sampleBuffer里拷贝出来的数据,提前invalidate很可能会触发不可预测的崩溃。另外,copyNextSampleBuffer()返回的CMSampleBuffer是需要手动管理引用计数的Core Foundation对象,Swift的ARC不会自动帮你释放它,这就是内存泄漏和应用崩溃的根源。
在Swift中手动释放CMSampleBuffer的方法
其实Swift里完全可以处理Core Foundation对象的引用计数,有两种靠谱的方式:
1. 手动调用CFRelease
CMSampleBuffer本质是CFTypeRef,你可以把它强制转换后调用CFRelease来释放内存。关键是要确保在完全使用完sampleBuffer之后再调用,推荐用defer语句来保证执行时机:
var outputSamples = [Float]() assetReader.startReading() while assetReader.status == .reading { let trackOutput = assetReader.outputs.first! guard let sampleBuffer = trackOutput.copyNextSampleBuffer(), let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else { continue } // 用defer确保不管代码执行路径如何,都会释放资源 defer { CMSampleBufferInvalidate(sampleBuffer) CFRelease(sampleBuffer as CFTypeRef) } let blockBufferLength = CMBlockBufferGetDataLength(blockBuffer) let sampleLength = CMSampleBufferGetNumSamples(sampleBuffer) * channelCount(from: assetReader) var data = Data(capacity: blockBufferLength) data.withUnsafeMutableBytes { (blockSamples: UnsafeMutablePointer<Int16>) in CMBlockBufferCopyDataBytes(blockBuffer, atOffset: 0, dataLength: blockBufferLength, destination: blockSamples) let processedSamples = process(blockSamples, ofLength: sampleLength, from: assetReader, downsampledTo: targetSampleCount) outputSamples += processedSamples } }
2. 用autoreleasepool包裹循环体
copyNextSampleBuffer()返回的对象默认是在自动释放池中的,如果你把每次循环的逻辑放在autoreleasepool里,就能让系统在每次循环结束后及时释放这些临时对象,避免内存堆积:
var outputSamples = [Float]() assetReader.startReading() while assetReader.status == .reading { autoreleasepool { let trackOutput = assetReader.outputs.first! guard let sampleBuffer = trackOutput.copyNextSampleBuffer(), let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else { return } defer { CMSampleBufferInvalidate(sampleBuffer) } let blockBufferLength = CMBlockBufferGetDataLength(blockBuffer) let sampleLength = CMSampleBufferGetNumSamples(sampleBuffer) * channelCount(from: assetReader) var data = Data(capacity: blockBufferLength) data.withUnsafeMutableBytes { (blockSamples: UnsafeMutablePointer<Int16>) in CMBlockBufferCopyDataBytes(blockBuffer, atOffset: 0, dataLength: blockBufferLength, destination: blockSamples) let processedSamples = process(blockSamples, ofLength: sampleLength, from: assetReader, downsampledTo: targetSampleCount) outputSamples += processedSamples } } }
关键注意点
- 永远不要在还在使用sampleBuffer数据的闭包(比如
withUnsafeMutableBytes)里调用CMSampleBufferInvalidate或CFRelease,否则会导致野指针访问,直接触发崩溃。 defer语句是Swift里保证资源释放的最佳实践,它会在当前作用域结束时自动执行,不管是正常完成还是抛出错误。- 对于带
copy前缀的Core Foundation API返回的对象,都需要手动管理引用计数,这是内存泄漏的高发区。
内容的提问来源于stack exchange,提问作者DmitryoN
相关产品推荐
相关产品推荐

