0

I am trying to access the keys and values in a CFDictionary which is created from a CGImageSource. Here is the definition of the function used to access Keys and Values data:

func CFDictionaryGetKeysAndValues(_ theDict: CFDictionary!, 
                                  _ keys: UnsafeMutablePointer<UnsafeRawPointer?>!, 
                                  _ values: UnsafeMutablePointer<UnsafeRawPointer?>!)

My problem is with creating the two pointers for the Keys and Values arrays. Also, I am really unsure whether I am declaring the Arrays the correct way. Here is the description of the CFDictionaryGetKeysAndValues keys and values parameters:

A C array of pointer-sized values that, on return, is filled with keys from the theDict. The keys and values C arrays are parallel to each other (that is, the items at the same indices form a key-value pair from the dictionary). This value must be a valid pointer to a C array of the appropriate type and size (that is, a size equal to the count of theDict), or NULL if the keys are not required. If the keys are Core Foundation objects, ownership follows the The Get Rule.

Here is my implementation according to what I understand of the Unsafe pointers API, but it gives me unreliable results:

// Get the CFDictionary and count the number of returned properties
let imageProperties: CFDictionary = CGImageSourceCopyProperties(imageSource, nil)!
let propCount = CFDictionaryGetCount(imageProperties)

// Create a stub UnsafeRawPointer used to initialize Array
let string: String = ""
let stubPointer = UnsafeRawPointer(string)

// Initialize an Array of pointers for the Keys and one for the Values
var aKeys: Array<UnsafeRawPointer> = Array(repeating: stubPointer, count: propCount)
var aValues: Array<UnsafeRawPointer> = Array(repeating:  stubPointer, count: propCount)

// Create the pointers and !hopefully! access dictionary Keys and Values
withUnsafePointer(to: &aKeys[0], {(ptr: UnsafePointer<UnsafeRawPointer>?) -> Void in
    var ptrKeysM = UnsafeRawPointer(ptr)
    let ptrKeys = UnsafeMutablePointer(&ptrKeysM)!
    ptrKeys.initialize(to: ptrKeysM, count: propCount)

    withUnsafePointer(to: &aValues[0], {(ptr2: UnsafePointer<UnsafeRawPointer>?) -> Void in
        var ptrValuesM = UnsafeRawPointer(ptr2)
        let ptrValues = UnsafeMutablePointer(&ptrValuesM)!
        ptrValues.initialize(to: ptrValuesM, count: propCount)            

        CFDictionaryGetKeysAndValues(imageProperties, ptrKeys, ptrValues)

    })
})

Now this compiles correctly. When the CGImageSource only returns 1 property, everything goes well (ie: the application doesn't crash). However, if the CGImageSource returns more than 1 property, I get the following error after the call to CFDictionaryGetKeysAndValues:

error: memory read failed for 0x1f600 
Thread 1: EXC_BAD_ACCESS (code=1, address=0x1f637)

You might have guessed that I am totally new to Swift and I still have not wrapped my head around this API for pointers. From what I understand, the GetKeysAndValues call returns pointers to the Keys and Values which are stored in the aKeys and aValues Arrays. Is this correct? Thank you all for your help.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • 1
    Just bridge the `CFDictionary` to a native Swift dictionary, compare https://stackoverflow.com/q/42786613/2976878 – Hamish Nov 22 '17 at 00:58
  • I'll check it out and update. Thank you. However, if anybody can still help me understand this, it would be very appreciated. I'd like to wrap my head around how the Unsafe pointers work. – Alexandre Sps Nov 22 '17 at 01:03
  • There's a few things wrong with what you're doing, dereferencing `ptrKeys` and `ptrValues` is undefined behaviour, as the pointer to `ptrKeysM` is only valid for the duration of the `UnsafeMutablePointer` initialiser call (really I wish the type checker would warning on that). But even if that did work, it's a pointer to the *variable* `ptrKeysM`, not to your buffer; you should've gone with `aKeys.withUnsafeBufferPointer`. But even then, filling up the arrays with dangling pointers is *pretty iffy* and I would stay clear of doing that. – Hamish Nov 22 '17 at 01:35
  • I would instead allocate uninitialised memory myself (w/ `UnsafeMutablePointer` & `UnsafeMutableBufferPointer`), and then pass off those pointers to `CFDictionaryGetKeysAndValues` and then bridge the keys + values back with `Unmanaged` + `as?` casts. Something like this: https://gist.github.com/hamishknight/5d2f7e0883ffbc870ed42fa77ddee0bd. But as said earlier, this is all totally redundant because Swift can do this bridging for you :) – Hamish Nov 22 '17 at 01:35
  • Thanks for your feedback Hamish. I ended up using NSDictionary like you suggested. – Alexandre Sps Nov 24 '17 at 02:52

0 Answers0