我想从我的ObjC调用一个C函数。我在C结构中传递一个C函数引用(在ObjC中定义),以便C可以调用该函数。我还想将完成块的引用传递给C,这样当我得到一个回调时,我可以调用该完成块。但我不知道如何实现这一点。根据我尝试的不同类型转换,我得到了不同的错误。
//Abc.m
void myCallback(MyData *data)
{
//I get the control here!
//((__bridge void *)(data->completionBlock))([NSString stringWithCString:data->json]); //how to call the completion block?
}
- (void)myMethod:(NSString)input
completion:(void (^)(NSString * _Nullable response))completionBlock
{
MyData *data = malloc(sizeof(MyData));
data->myCallback = myCallback;
data->completionBlock = (__bridge void *)(completionBlock);//is this correct?
cFunction(data);
}
//Xyz.c
typedef struct
{
char *json;
void (*myCallback)(void *response);
void *completionBlock;
} MyData;
void cFunction(MyData *data)
{
data->json = "some response";
(data->myCallback)(data);
}
答案 0 :(得分:1)
有两个问题需要考虑:
(__bridge void *)(data->completionBlock)
不会返回块类型,因此编译器将拒绝该调用。按照您的代码设计,我们首先定义一个类型,以简化操作:
typedef void (^CompletionBlock)(NSString * _Nullable response);
这样你的myMethod
函数就像以前一样开始:
- (void)myMethod:(NSString*)input
completion:(CompletionBlock)completionBlock
{
MyData *data = malloc(sizeof(MyData));
data->myCallback = myCallback;
现在,您必须将块存储到结构中,同时确保ARC不会在Objective-C端释放它。为此,您使用__bridge_retain
返回对块的保留引用,您的代码将负责平衡保留。这可以在您的C代码中完成,也可以将所有权转移回ARC并让它来处理它。所以myMethod
的剩余部分是:
data->completionBlock = (__bridge_retained void *)(completionBlock);
cFunction(data);
}
现在您的cFunction
只调用了您的myCallBack
功能,因此在这种情况下无需更改任何内容。
现在转到myCallBack
,首先我们修复了类型不匹配问题,并将其定义为void *
,然后恢复MyData *
:
void myCallback(void *response)
{
MyData *data = response;
现在我们需要恢复阻止。我们可以将它转换为块类型,但这样我们就可以在使用它之后将它释放出来(使用Block_release()
);但是我们可以使用__bridge_transfer
将所有权交还给ARC,以便管理它:
CompletionBlock completionBlock = (__bridge_transfer CompletionBlock)data->completionBlock;
现在我们将字符串输出并将其转换为NSString
:
NSString *result = [NSString stringWithCString:data->json encoding:NSUTF8StringEncoding];
然后释放malloc'ed包装器:
free(data);
最后我们称之为阻止:
completionBlock(result);
}
以上是你的设计,但是没有必要让你的C函数调用Objective-C文件中的另一个C函数来调用块 - 块是C语言特性并且在.c
文件中受支持铛。您可以将data->completionBlock
转换为块类型,调用它,然后使用Block_release()
释放块。
此外,由于块是C类型,您可以使用块类型键入struct field completionBlock
并删除大量的强制转换(但这些情况在运行时无需任何成本)。
HTH
答案 1 :(得分:0)
__bridge
表示使用c样式指针,其行为如assign
或__unsafe_unretained
。
您使用它意味着data->completionBlock
只指向completionBlock
而不保留,因此您可能会在completionBlock
被释放后崩溃。
如果您想使用ARC访问struct中的block或objective-c对象,则必须使用__unsafe_unretained
声明这些对象并自行管理它们。
typedef struct {
char *json;
__unsafe_unretained NSString *name;
__unsafe_unretained CompletionBlock block;
} SampleStruct;