25

それで、今日は退屈していて、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;
}

これが何をするかを要約します:

  1. クラスペアを割り当てます
  2. すべてのクラスメソッドとインスタンスメソッドをオブジェクトに追加します
  3. クラスペアを登録します

ご覧のとおり、これはあまり柔軟ではありませんが、機能し、双方向のストリートです。

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にはいくつかの追加のブリッジキャストが必要です。

それで、私は本当の質問に行きます:
この設計構造の賛否両論は何ですか?私は頭のてっぺんからいくつかをリストすることができます:

長所:

  1. C++による演算子のオーバーロード
  2. ObjCとの動的メソッドバインディング
  3. C++またはObjCのいずれかの方法で構築できます

短所:

  1. 読みにくい実装
  2. インターフェイスに追加されるすべてのC++実装に対して、セレクターとバインディングを追加する必要があります
  3. クラスオブジェクトを直接参照することはできません

それで、結局のところ、アプリケーションでこの設計構造をお勧めしますか?なぜ。

4

1 に答える 1

23

では、結局のところ、この設計構造をアプリケーションで推奨できますか? なぜ。

いいえ。

これは非常に優れたコードです。私は特に imp_implementationWithBlock() の使用が好きです (ただし、ランタイムのその特定の機能に部分的である可能性があることは認めます;)。そしてもちろん、このような探索は常に非常に価値のある学習ツールです。

「実世界の有料プロジェクト」の使用のコンテキストでの問題は、典型的な C++ ライブラリまたは典型的な Objective-C APIs/ライブラリ。別の言い方をすれば、2 つの既存のランタイムの融合から派生した新しいランタイムを効果的に作成したことになります。

そして、短所で指摘したように、このパターンに取り入れたいすべての C++ クラスの上に shim をタッチ、ラップ、変更、および/またはデバッグする必要があります。

過去 20 年以上にわたりかなりの量の Objective-C++ コードを扱ってきた中で、このようなブリッジは一般的に価値があるよりも面倒です。コードの作成とデバッグに費やす時間を減らして、C++ (または率直に言って C) API の周りに単純な Objective-C ラッパーを作成し、ターゲット システムの Objective-C フレームワークと統合して使用できるようにしたほうがよいでしょう。

于 2012-04-04T16:39:45.017 に答える