将 ObjC id<Protocol> 和类型化的对象强制转换为 C 指针(void *)然后将其转换回去是否安全?



我正在为 Metal API (http://github.com/recp/cmtl( 开发 C 包装器/绑定。

我将 Objective-C 类型定义为 void 在 C 标头中,例如typedef void MtDevice。然后,我将在 ObjC 函数中分配的对象转换为void*指针。然后将其转换回 ObjC 以调用 ObjC 函数。

这是我的代码:

弧形风格:

MtDevice*
mtDeviceCreat() {
id<MTLDevice> mdevice;
mdevice = MTLCreateSystemDefaultDevice();
return (void *)CFBridgingRetain(mdevice);
}
MtCommandQueue*
mtCommandQueue(MtDevice *device) {
id<MTLDevice>       mdevice;
id<MTLCommandQueue> mcmdQueue;
mdevice   = (__bridge id<MTLDevice>)device;
mcmdQueue = [mdevice newCommandQueue];
return (void *)CFBridgingRetain(mcmdQueue);
}

电弧禁用:

MtDevice*
mtDeviceCreat() {
id<MTLDevice> mdevice;
mdevice = MTLCreateSystemDefaultDevice();
return [mdevice retain];
}
MtCommandQueue*
mtCommandQueue(MtDevice *device) {
id<MTLDevice>       mdevice;
id<MTLCommandQueue> mcmdQueue;
mdevice   = (__strong id<MTLDevice>)device;
mcmdQueue = [mdevice newCommandQueue];
return [mcmdQueue retain];
}

我正在考虑禁用 ARC,所以我将其转换为第二个版本。这里有几个问题:

  1. newCommandQueue和类似功能之后,我应该在返回对象之前保留对象吗?
  2. 我用mdevice = (__strong id<MTLDevice>)device;将 void* 投射到 ObjC 类型,但是这个呢:mdevice = device;?编译器似乎没有抱怨。
  3. 对于 ARC 和非 ARC 中的转换,像我一样在 ObjC 类型和 C void* 之间进行转换是安全的吗?

PS:我的 C 函数只是从 C 调用 ObjC 函数的包装器。我不是在尝试访问 C 函数中的 ObjC 类成员。

编辑:

发布代码:

void
mtRelease(void *obj) {
[(id)obj release];
}

编辑2:

更新的代码(已禁用 ARC(:

MtDevice*
mtDeviceCreat() {
return MTLCreateSystemDefaultDevice();
}
MtCommandQueue*
mtCommandQueue(MtDevice *device) {
return [(id<MTLDevice>)device newCommandQueue];
}

在这两种情况下,对-retain的调用不正确。MTLCreateSystemDefaultDevice()遵循创建规则,因此已为您保留该规则。您负责一个(自动(发布以平衡原始创作,加上您执行的每个-retain一个。

同样,-newCommandQueue返回一个已保留且最终必须释放的对象。任何以"new"开头的方法都是如此。

禁用 ARC 时,__strong不执行任何操作。mdevice = device;很好。

将 Objective-C 对象指针投射到void*并返回是"安全的"(除了失去类型安全(。我建议您使用指向不透明结构类型的指针而不是void*,以保持类型安全。例如,typedef struct MtDevice *MtDeviceRef;,其中从未定义struct MtDevice。这就是苹果定义自己的类型的方式,例如CFStringRef.

相关内容