I am using a third-party library that requires a C callback in an iOS program. The callback provides a void* userdata parameter that I can use as I wish. In this callback I want to create an NSData object that is returned to the code that invokes the API that eventually calls the callback. For example:
// C callback:
static void callback(int x, void* userdata)
{
    if (userdata)
    {
        // This gives an error:
        // Pointer to non-const type 'NSData*' with no explicit ownership
        NSData** p = (NSData**)userdata;
        *p = [NSData init:...];
    }
}
// main code:
NSData* d = ...;  // sometimes valid, sometimes nil
library_api(callback, &d);  // have the callback initialize or replace d
if (d)
{
    [d doSomthing:...];
}
I am using ARC, which I think is at the root of the issues. I thought of using a struct to hold the NSData*, but ARC forbids Objective-C objects in structs or unions. There is probably some variant of __bridge that might help, but I am not sure which. I have looked at a lot of SO questions and read the Apple docs, but it is not clear to me how to code this up.
Ideally, if d were not nil at the time the callback is invoked, its current value would be released and a new NSData object would replace it. In other words, when library_api completes, d always holds the NSData object created by the callback, and any previously held value would have been released properly.
I suppose I could keep the Objective-C code out of the callback and just malloc() a buffer to store the data which would then be copied to an NSData object in the main code, but I am hoping that I can avoid that extra step.
You can't transfer object ownership to a void*, but you can transfer object ownership to a CoreFoundation type, like CFDataRef or CFTypeRef.  You're then free to pass around the CF pointer as you wish.  
e.g.:
NSData* data = ...
CFTypeRef cfData = (__bridge CFTypeRef)data; // If you want to pass in data as well.
library_api(callback, &cfData);
if (cfData)
{
    data = (__bridge_transfer NSData*)cfData; // Transfer ownership to ARC.  No need to call CFRelease(cfData).
    NSLog@("Data: %@", data);
}
And in the callback:
static void callback(int x, void* userdata)
{
    if (userdata)
    {
        CFTypeRef* cfDataPtr = userdata;
        CFTypeRef cfInData = *cfDataPtr;
        NSData* inData = (__bridge NSData*)cfInData;
        NSLog(@"In Data: %@", inData);
        NSData* outData = [[NSData alloc] init];
        CFTypeRef cfOutData = (__bridge_retained CFTypeRef)outData; // Transfer ownership out of ARC.
        *cfDataPtr = cfOutData;
    }
}
                        You can have struct contain ObjC object, they just have to be __unretained_unsafe OR in .mm file.
There are few options:
1 Use a struct to hold ObjC object in .m file
struct MyStruct
{
    __unretained_unsafe id obj;
}
2 Use a struct to hold ObjC object in .mm file (Objective-C++) and use C++ new/delete for the struct. You can do this because compiler will generate retain/release in the constructor/destructor of the struct.
struct MyStruct
{
    id obj; // no need to have __unretained_unsafe
}
3 Change the signature of the callback function and cast it (I didn't try this before)
4 Just make this file non ARC with -fno-objc-arc compiler flag
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