UnsafeMutablePointer vs UnsafeMutablePointer<Unmanaged>

This question is derived from use of the CoreVideo framework but ultimately is about Swift types and interoperability with a C library. Hopefully others agree it's relevant enough for this forum.

In the CoreVideo APIs listed below, two very similar functions have slightly different Swift signatures that I don't understand. When used in Objective-C, both APIs take a pointer to a CVPixelBufferRef as their last parameter, but in Swift they take slightly different types.

Based on the old Create/Get ownership rules, both APIs vend a CVPixelBufferRef that the caller is responsible for deallocating so I'm not sure why the second API requires the additional use of Unmanaged whereas the first does not.

How do UnsafeMutablePointer<CVPixelBuffer?> and UnsafeMutablePointer<Unmanaged<CVPixelBuffer?>> differ in this case and why is one used over the other?

C Interface:

CV_EXPORT CVReturn CVPixelBufferCreate(
  CFAllocatorRef CV_NULLABLE allocator,
  size_t width,
  size_t height,
  OSType pixelFormatType,
  CFDictionaryRef CV_NULLABLE pixelBufferAttributes,
  CV_RETURNS_RETAINED_PARAMETER CVPixelBufferRef CV_NULLABLE * CV_NONNULL pixelBufferOut
)

CV_EXPORT CVReturn CVPixelBufferCreateWithIOSurface(
  CFAllocatorRef CV_NULLABLE allocator,
  IOSurfaceRef CV_NONNULL surface,
  CFDictionaryRef CV_NULLABLE pixelBufferAttributes,
  CVPixelBufferRef CV_NULLABLE * CV_NONNULL pixelBufferOut
)

Swift Interface:

public func CVPixelBufferCreate(
  _ allocator: CFAllocator?, 
  _ width: Int, 
  _ height: Int, 
  _ pixelFormatType: OSType, 
  _ pixelBufferAttributes: CFDictionary?, 
  _ pixelBufferOut: UnsafeMutablePointer<CVPixelBuffer?>
) -> CVReturn


public func CVPixelBufferCreateWithIOSurface(
  _ allocator: CFAllocator?, 
  _ surface: IOSurfaceRef, 
  _ pixelBufferAttributes: CFDictionary?, 
  _ pixelBufferOut: UnsafeMutablePointer<Unmanaged<CVPixelBuffer>?>
) -> CVReturn

// This work as expected. 
var allocatedPixelBuffer: CVPixelBuffer?
CVPixelBufferCreate(...., &allocatedPixelBuffer)

// This compiles:
var allocatedPixelBuffer: Unmanaged<CVPixelBuffer>?
CVPixelBufferCreateWithIOSurface(...., &allocatedSurfaceBuffer)

// But do I then call: 
let pixelBuffer = allocatedPixelBuffer.takeRetained()   // I assume this as `Create` rule applies?
let pixelBuffer = allocatedPixelBuffef.takeUnretained()

How do UnsafeMutablePointer<CVPixelBuffer?> and
UnsafeMutablePointer<Unmanaged<CVPixelBuffer?>> differ in this case

I’ll come back to this below.

and why is one used over the other?

Because in the CVPixelBufferCreate case the parameter is decorated with CV_RETURNS_RETAINED_PARAMETER and in the CVPixelBufferCreateWithIOSurface case it’s not. Thus, Swift has no idea how to handle the memory management in the second case and that’s why it’s wrapped in an Unmanaged.

Based on the old Create/Get ownership rules, both APIs vend a
CVPixelBufferRef that the caller is responsible for deallocating so
I'm not sure why the second API requires the additional use of
Unmanaged whereas the first does not.

The Create / Get rule applies to the function result, and in both of these cases the function result is of type CVReturn, which is not an object. Thus, the Create / Get rule doesn’t apply here.

As to how you handle this, in the second case you’ll need a takeRetained call to get you from unmanaged space into managed space. Personally I’d wrap it in something like this:

func myCVPixelBufferCreateWithIOSurface(
    _ allocator: CFAllocator?,
    _ surface: IOSurfaceRef,
    _ pixelBufferAttributes: CFDictionary?,
    _ pixelBufferOut: UnsafeMutablePointer<Unmanaged<CVPixelBuffer>?>
) throws -> CVPixelBuffer {
    var pixelBufferQ: Unmanaged<CVPixelBuffer>? = nil
    let err = CVPixelBufferCreateWithIOSurface(
        allocator,
        surface,
        pixelBufferAttributes,
        &pixelBufferQ
    )
    guard err == kCVReturnSuccess else {
        … wrap and throw the error …
    }
    let pixelBuffer = pixelBufferQ!.takeRetainedValue()
    return pixelBuffer
}

Oh, and feel free to file a bug against CVPixelBufferCreateWithIOSurface requesting that it be decorated correctly.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

All these years later and still learning...

So the issue here is really just the lack of the decorator in the C interface? Otherwise the two functions are functionally the same when called from Swift, with the acknowledgement that .takeRetainedValue() is required because the decorator is missing.

Thank you.