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?
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()
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:
CF_RETURNS_RETAINED
or CF_RETURNS_NOT_RETAINED
to tell the compiler what memory behavior is neededNSArray
and return that--it'll automatically become an [AnyObject]
array in Swift. If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With