それで、今日は退屈していて、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();
};
ご覧のとおり、インターフェースは非常に単純で、理解しやすいものです。2つの(ほぼ)同一のインターフェースを作成します。1つはC ++オブジェクト用で、もう1つは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();
}), "v@:");
class_addMethod(NSCPPObj_class, @selector(somethingThatReturnsAValue), imp_implementationWithBlock(^(id self) {
return ((struct NSCPPObj *) self)->somethingThatReturnsAValue();
}), "i@:");
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にはいくつかの追加のブリッジキャストが必要です。
それで、私は本当の質問に行きます:
この設計構造の賛否両論は何ですか?私は頭のてっぺんからいくつかをリストすることができます:
長所:
- C++による演算子のオーバーロード
- ObjCとの動的メソッドバインディング
- C++またはObjCのいずれかの方法で構築できます
短所:
- 読みにくい実装
- インターフェイスに追加されるすべてのC++実装に対して、セレクターとバインディングを追加する必要があります
- クラスオブジェクトを直接参照することはできません
それで、結局のところ、アプリケーションでこの設計構造をお勧めしますか?なぜ。