Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use CFArrayRef in Swift?

I'm using an Objective-C class in my Swift project via a bridging header. The method signature looks something like this:

- (CFArrayRef)someMethod:(someType)someParameter;

I started by getting an instance of the class, calling the method, and storing the value:

var myInstance = MyClassWithThatMethod();
var cfArr = myInstance.someMethod(someValue);

Then try to get a value in the array:

var valueInArrayThatIWant = CFArrayGetValueAtIndex(cfArr, 0);

However I get the error Unmanaged<CFArray>' is not identical to 'CFArray'. What does Unmanaged<CFArray> even mean?

I looked through How to convert CFArray to Swift Array? but I don't need to convert the array to a swift array (however that would be nice). I just need to be able to get values from the array.

I have also tried the method of passing the CFArray into a function outlined in this answer:

func doSomeStuffOnArray(myArray: NSArray) {

}

However I get a similar error when using it:

doSomeStuffOnArray(cfArr); // Unmanaged<CFArray>' is not identical to 'NSArray'

I am using CFArray because I need to store an array of CGPathRef, which cannot be stored in NSArray.

So how am I supposed to use CFArray in Swift?

like image 680
Andrew Avatar asked Jan 10 '23 03:01

Andrew


2 Answers

As explained in Working with Core Foundation Types, there are two possible solutions when you return a Core Foundation object from your own function that is imported in Swift:

  • Annotate the function with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED. In your case:

    - (CFArrayRef)someMethod:(someType)someParameter CF_RETURNS_NOT_RETAINED;
    
  • Or convert the unmanaged object to a memory managed object with takeUnretainedValue() or takeRetainedValue() in Swift. In your case:

    var cfArr = myInstance.someMethod(someValue).takeUnretainedValue()
    
like image 173
Martin R Avatar answered Jan 16 '23 21:01

Martin R


An Unmanaged is a wrapper for an actual CF value. (Sort of like an optional.) It's there because ARC can't tell from looking at the declaration of someMethod: whether that method retains the value it returns.

You unwrap an Unmanaged by telling ARC what memory management policy to use for the value inside. If someMethod calls CFRetain on its return value:

let cfArr = myInstance.someMethod(someValue).takeRetainedValue()

If it doesn't:

let cfArr = myInstance.someMethod(someValue).takeUnretainedValue()

After you do that, cfArr is a CFArray, so you can use the bridging tricks from the other questions you linked to for accessing it like a Swift array.

If you own the code for someMethod you can change it a bit to not need this. There's a couple of options for that:

  • Annotate with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED to tell the compiler what memory behavior is needed
  • Since it's an ObjC method, bridge to NSArray and return that--it'll automatically become an [AnyObject] array in Swift.
like image 33
rickster Avatar answered Jan 16 '23 20:01

rickster