Objective-C - Недостатки для связывания с С++?
Итак, мне было скучно сегодня, и я решил испортить интерполяцию С++/Obj-C, и я нашел способ создать очень интересную настройку.
@protocol NSCPPObj <NSObject>
-(id) init;
-(id) initWithInt:(int) value;
-(int) somethingThatReturnsAValue;
-(void) doSomething;
@end
class NSCPPObj : objc_object {
public:
static Class cls();
int iVar;
NSCPPObj();
NSCPPObj(int);
int somethingThatReturnsAValue();
void doSomething();
};
Как вы можете видеть, интерфейс довольно прост и понятен. Мы создаем два (почти) идентичных интерфейса, один для объекта С++, а другой для протокола Obj-C.
Теперь я нашел способ реализовать это, но привязывай себя, это становится уродливым:
// NSCPPObj.mm
#import <objc/runtime.h>
#import <iostream>
#import "NSCPPObject.h"
Class NSCPPObj_class = nil;
__attribute__((constructor))
static void initialize()
{
NSCPPObj_class = objc_allocateClassPair([NSObject class], "NSCPPObj", 0);
class_addMethod(NSCPPObj_class->isa, @selector(alloc), imp_implementationWithBlock(^(id self) {
return class_createInstance(NSCPPObj_class, sizeof(struct NSCPPObj));
}), "@@:");
class_addMethod(NSCPPObj_class, @selector(init), imp_implementationWithBlock(^(id self) {
return self;
}), "@@:");
class_addMethod(NSCPPObj_class, @selector(initWithInt:), imp_implementationWithBlock(^(id self, int value) {
((struct NSCPPObj *) self)->iVar = value;
return self;
}), "@@:i");
class_addMethod(NSCPPObj_class, @selector(doSomething), imp_implementationWithBlock(^(id self) {
((struct NSCPPObj *) self)->doSomething();
}), "[email protected]:");
class_addMethod(NSCPPObj_class, @selector(somethingThatReturnsAValue), imp_implementationWithBlock(^(id self) {
return ((struct NSCPPObj *) self)->somethingThatReturnsAValue();
}), "[email protected]:");
objc_registerClassPair(NSCPPObj_class);
}
Class NSCPPObj::cls()
{
return NSCPPObj_class;
}
NSCPPObj::NSCPPObj()
{
this->isa = NSCPPObj_class;
[((id<NSCPPObj>) this) init];
}
NSCPPObj::NSCPPObj(int value)
{
this->isa = NSCPPObj_class;
[((id<NSCPPObj>) this) initWithInt:value];
}
void NSCPPObj::doSomething()
{
std::cout << "Value Is: " << [((id<NSCPPObj>) this) somethingThatReturnsAValue] << std::endl;
}
int NSCPPObj::somethingThatReturnsAValue()
{
return iVar;
}
Я обобщу, что это делает:
- Выделяет классную пару
- Добавляет все методы класса и экземпляра к объекту
- Регистрирует класс Пара
Теперь, как вы видите, это не очень гибко, но он работает, и это улица с двусторонним движением:
id<NSCPPObj> obj = [[NSCPPObj::cls() alloc] initWithInt:15];
[obj doSomething];
NSLog(@"%i", [obj somethingThatReturnsAValue]);
NSLog(@"%@", obj);
NSCPPObj *objAsCPP = (__bridge NSCPPObj *) obj;
objAsCPP->doSomething();
std::cout << objAsCPP->somethingThatReturnsAValue() << std::endl;
Вы также можете создать объект с помощью new NSCPPObj(15)
, но не забудьте удалить его!
Очевидно, что это может работать в среде ARC или без ARC, но для ARC требуется несколько дополнительных мостов.
Итак, я пришел к реальному вопросу:
Каковы плюсы и минусы этой структуры дизайна? Я могу перечислить несколько из них:
Плюсы:
- Перегрузка оператора с помощью С++
- Связывание динамического метода с ObjC
- Может быть сконструирован в моделях С++ или ObjC
Минусы:
- Трудная для чтения реализация
- Селекторы и привязки должны быть добавлены для каждой реализации С++, добавленной в интерфейс
- Нельзя напрямую ссылаться на объект класса
Итак, после всего этого вы бы рекомендовали эту структуру дизайна в приложении? и почему.
Ответы
Ответ 1
Итак, после всего этого вы бы рекомендовали эту структуру дизайна в выражение? и почему.
Нет.
Это действительно хороший код; Мне особенно нравится использование imp_implementationWithBlock() (но я допускаю, что я мог бы быть частичным для этой конкретной функции среды выполнения;). И, конечно, исследования, подобные этому, всегда являются невероятно ценным инструментом обучения.
Проблема в контексте использования проекта "реальный мир" заключается в том, что вы эффективно создаете относительно общий мост, который затем должен иметь определенные мосты с обоих концов для взаимодействия с типичными библиотеками С++ или типичными Objective-C API/библиотеки. Другими словами, вы создали новую рабочую среду, полученную из объединения двух существующих сред выполнения.
И, как вы указываете в "Минутах", вам в значительной степени нужно прикоснуться, обернуть, изменить и/или отладить прокладку поверх каждого класса С++, который вы хотите ввести в этот шаблон.
При работе с довольно небольшим количеством кода Objective-C ++ за последние 20+ лет такой мост, как правило, больше проблем, чем того стоит. Скорее всего, вам будет лучше - потратьте меньше времени на написание и отладку кода - создание простых оберток Objective-C вокруг С++ (или C, откровенно) API, которые затем могут быть интегрированы и использованы инфраструктурой целевой системы Objective-C.