C回调如何通过userdata指针返回Objective-C对象?

Ran*_*ook 8 objective-c callback ios automatic-ref-counting

我正在使用第三方库,需要在iOS程序中进行C回调.回调提供了一个void*我可以随意使用的userdata参数.在这个回调中,我想创建一个NSData对象,该对象返回到调用最终调用回调的API的代码.例如:

// 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:...];
}
Run Code Online (Sandbox Code Playgroud)

我正在使用ARC,我认为这是问题的根源.我想过使用一个结构来保存NSData*,但ARC禁止在结构或联合中使用Objective-C对象.可能有一些变体__bridge可能有所帮助,但我不确定哪个.我已经查看了很多SO问题并阅读了Apple文档,但我不清楚如何对其进行编码.

理想情况下,如果在调用回调时d不是nil,则会释放其当前值,并且新的NSData对象将替换它.换句话说,当library_api完成时,d总是保存由回调创建的NSData对象,并且任何先前保持的值都将被正确释放.

我想我可以将Objective-C代码保留在回调之外,只是malloc()一个缓冲区来存储数据,然后将其复制到主代码中的NSData对象,但我希望我能避免这一额外的步骤.

Dar*_*ren 4

您无法将对象所有权转移到 a void*,但可以将对象所有权转移到 CoreFoundation 类型,例如CFDataRefCFTypeRef。然后您就可以随意传递 CF 指针。

例如:

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);
}
Run Code Online (Sandbox Code Playgroud)

并在回调中:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)