Objective-C - 使用C ++进行桥接的缺点?

时间:2012-04-04 15:48:44

标签: c++ objective-c ios cocoa objective-c++

所以,今天我很无聊,决定搞乱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();
};

正如您所看到的,界面非常简单易懂。我们创建两个(几乎)相同的接口,一个用于C ++对象,另一个用于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. 注册班级对
  4. 现在,正如您所看到的,这不是很灵活,但确实有效,而且它是双向的:

    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方式构建
    4. 缺点:

      1. 难以阅读的实施
      2. 选择器&amp;必须为添加到接口的每个C ++实现添加绑定
      3. 无法直接引用类对象
      4. 那么,毕竟,你会在应用程序中推荐这个设计结构吗?以及为什么。

1 个答案:

答案 0 :(得分:23)

  

所以,毕竟,你会推荐这个设计结构吗?   应用?为什么。

没有

这是一个非常好的代码;我特别喜欢使用imp_implementationWithBlock()(但我承认我可能偏向于运行时的特定功能;)。当然,像这样的探索总是一种非常有价值的学习工具。

在“真实世界支付项目”使用的上下文中,问题在于您正在有效地创建一个相对通用的桥接器,然后必须在任一端具有特定的桥接器以与典型的C ++库或典型的目标接口。 C API /库。换句话说,您已经有效地创建了一个从两个现有运行时合并而来的新运行时。

而且,正如你在Cons中所指出的那样,你几乎必须触摸,包装,修改和/或调试你希望带入这种模式的每个C ++类的填充程序。

在过去的20多年里,使用相当多的Objective-C ++代码,这样的桥梁通常比它的价值更麻烦。您可能会更好 - 花更少的时间编写和调试代码 - 围绕C ++(或C,坦率地)API创建简单的Objective-C包装,然后可以与目标系统的Objective-C框架集成并使用它们。