Безопасно ли приводить ObjC id <Protocol>и набирать объекты к указателю C (void *), а затем возвращать его обратно? - PullRequest
0 голосов
/ 28 июня 2018

Я работаю над оболочками / привязками C для Metal API (http://github.com/recp/cmtl).

Я определил типы Objective-C как пустые в заголовках языка Си, например typedef void MtDevice. Затем я приведу объект, выделенный в функции ObjC, к указателю void *. Затем приведем его обратно к ObjC для вызова функции ObjC.

Вот мой код [с]:

ARC стиль:

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);
}

ARC-инвалидов:

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 являются только оболочками для вызова функций ObjC из C. Я не пытаюсь получить доступ к членам класса ObjC в функциях C.

EDIT:

Код выпуска:

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

РЕДАКТИРОВАТЬ 2:

Обновленный код (ARC отключен):

MtDevice*
mtDeviceCreat() {
  return MTLCreateSystemDefaultDevice();
}

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

1 Ответ

0 голосов
/ 28 июня 2018

Звонки на -retain неверны в этих двух случаях. MTLCreateSystemDefaultDevice() следует правилу создания , поэтому оно уже сохранено для вас. Вы несете ответственность за один (автоматический) релиз, чтобы сбалансировать исходное творение, плюс один за каждый -retain, который вы выполняете.

Аналогично, -newCommandQueue возвращает объект, который уже сохранен и который вы должны в конечном итоге освободить. Это верно для любого метода, начинающегося с "new" .

__strong ничего не делает, когда ARC отключен. mdevice = device; хорошо.

Приведение указателей объектов Objective-C к void* и обратно "безопасно" (за исключением потери безопасности типов). Я рекомендую использовать указатели для непрозрачных структурных типов вместо void*, чтобы обеспечить безопасность типов. Например, typedef struct MtDevice *MtDeviceRef;, где struct MtDevice никогда не определяется. Вот как Apple определяет свои собственные типы, такие как CFStringRef.

...