0

Foundation is chock full of functions that take an opaque void *info then later vend it back. In pre-ARC Objective C days, you could retain an object, supply it, then when it was handed back to your callback release it.

For example,

CGDataProviderRef CGDataProviderCreateWithData(void *info, const void *data, size_t size, CGDataProviderReleaseDataCallback releaseData);

typedef void (*CGDataProviderReleaseDataCallback)(void *info, const void *data, size_t size);

In this case, you could supply a retained object in info, then release it in the callback (after appropriate casting).

How would I do this in Swift?

David H
  • 40,852
  • 12
  • 92
  • 138
  • @vadian Yes, its the same technique. The problem is the title - how about you want to change something other than 'self'? but anyway, will delete my answer. – David H Jun 23 '17 at 18:04

1 Answers1

0

With assistance from Quinn 'The Eskimo' at Apple I found out how to do this. Given an object:

let pixelBuffer: CVPixelBuffer

get a pointer:

  1. Get an unmanaged object after retaining it:

    let fooU: Unmanaged = Unmanaged.passRetained(pixelBuffer)

  2. Convert it to a raw pointer

    let foo: UnsafeMutableRawPointer = fooU.toOpaque()

Recover the object while releasing it:

  1. Convert the raw pointer to an unmanaged typed object

    let ptr: Unmanaged<CVPixelBuffer> = Unmanaged.fromOpaque(pixelPtr)

  2. Recover the actual object while releasing it

    let pixelBuffer: CVPixelBuffer = ptr.takeRetainedValue()

The following code has been tested in an app. Note without Apple's help I'd never have figured this out thus the Q & A! Hope it helps someone!

Also, note the use of @convention(c), something I'd never seen before!

let fooU: Unmanaged = Unmanaged.passRetained(pixelBuffer)
let foo: UnsafeMutableRawPointer = fooU.toOpaque()
/* Either "bar" works */
/* let bar: @convention(c) (UnsafeMutableRawPointer?, UnsafeRawPointer, Int) -> Swift.Void = { */
let bar: CGDataProviderReleaseDataCallback = {
    (_ pixelPtr: UnsafeMutableRawPointer?, _ data: UnsafeRawPointer, _ size: Int) in

    if let pixelPtr = pixelPtr {
        let ptr: Unmanaged<CVPixelBuffer> = Unmanaged.fromOpaque(pixelPtr)
        let pixelBuffer: CVPixelBuffer = ptr.takeRetainedValue()
        CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
        DispatchQueue.main.async {
            print("UNLOCKED IT!")
        }
    }
}

let val: CVReturn = CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
if  val == kCVReturnSuccess,
    let sourceBaseAddr = CVPixelBufferGetBaseAddress(pixelBuffer),
    let provider = CGDataProvider(dataInfo: foo, data: sourceBaseAddr, size: sourceRowBytes * height, releaseData: bar)
{
    let colorspace = CGColorSpaceCreateDeviceRGB()
    let image = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: sourceRowBytes,
                    space: colorspace, bitmapInfo: bitmapInfo, provider: provider, decode: nil,
                    shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
    /* CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) */
    return image
} else {
    return nil
}

Quinn recently updated the Apple Forum thread on this, stating that somehow this technique never made it into either of the two Apple Swift Documents, and that he just entered a rdar to get it added. So you won't find this info anywhere else (well, at least now!)

David H
  • 40,852
  • 12
  • 92
  • 138