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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:13:01