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

如何对相机捕获回调中的CMSampleBuffer进行内存管理?

Hey there! Let's walk through the proper memory management for CMSampleBuffer when you need to use it outside the captureOutput(_:didOutput:from:) callback—this is a super common pitfall with AVFoundation, so great question.

Proper CMSampleBuffer Memory Management for Off-Scope Usage

Core Retain/Release Basics

First off, remember that CMSampleBuffer is a Core Foundation object, which means it follows manual memory management rules (ARC doesn't handle it automatically). Apple's docs are clear: if you need to reference this buffer outside the callback's scope, you have to explicitly retain it, then release it when you're done.

Here's how to adjust your code to do this safely:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 
    guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } 
    
    // Retain the buffer BEFORE passing it out of this scope
    CFRetain(sampleBuffer)
    
    // Example: Dispatch to a background queue for processing
    DispatchQueue.global(qos: .userInitiated).async {
        // Use the sampleBuffer (and its associated imageBuffer) here
        self.processCapturedBuffer(sampleBuffer)
        
        // Release the buffer once you're finished with it
        CFRelease(sampleBuffer)
    }
}

Critical Rules to Avoid Crashes & Leaks

  • Always pair CFRetain with CFRelease: Every retain must have a matching release. Skip a release, and you'll leak memory. Release too many times, and your app will crash.
  • Use defer for guaranteed cleanup: If your processing logic has multiple exit points (like early returns on errors), wrap the CFRelease in a defer block to ensure it runs no matter what. This is a Swift best practice for manual cleanup:
    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
        defer {
            // This runs even if we hit a guard failure below
            CFRelease(sampleBuffer)
        }
        
        guard let self = self, let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
        
        // Your processing logic here
    }
    
  • Don't hold onto buffers longer than needed: CMSampleBuffer objects are memory-heavy, especially at high capture resolutions/frame rates. Process them quickly and release immediately to keep your app's memory footprint low.

Performance Best Practices (As Apple Mentions)

To keep your capture pipeline smooth:

  • Offload processing to background queues: Never do heavy work (like image conversion, face detection) on the main thread or the capture callback thread. Keep the callback as lightweight as possible—only retain the buffer and dispatch it elsewhere.
  • Avoid retaining buffers in bulk: If you need to queue multiple frames for processing, make sure you're releasing them as soon as each is handled. A backlog of retained CMSampleBuffers can quickly eat up memory.
  • Know when to copy instead of retain: If you only need the underlying CVImageBuffer (pixel data) and not the entire sample buffer, you can create a copy of the pixel buffer (using CVPixelBufferCreate or related methods). This lets you release the original CMSampleBuffer faster, but it adds a copy cost—weigh this against your performance needs.

Full Safe Example

Here's a complete example that handles async processing, UI updates, and guaranteed cleanup:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 
    guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

    CFRetain(sampleBuffer)

    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
        defer {
            CFRelease(sampleBuffer)
        }

        guard let self = self else { return }

        // Convert CVImageBuffer to UIImage (example processing)
        let ciImage = CIImage(cvImageBuffer: imageBuffer)
        guard let cgImage = CIContext(options: [.useSoftwareRenderer: false]).createCGImage(ciImage, from: ciImage.extent) else { return }
        let capturedImage = UIImage(cgImage: cgImage)

        // Update UI on main thread
        DispatchQueue.main.async {
            self.previewImageView.image = capturedImage
        }
    }
}

内容的提问来源于stack exchange,提问作者Carpetfizz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:13:24