【问题标题】:Objective-C - Disadvantages to Bridging With C++?Objective-C - 与 C++ 桥接的缺点?
【发布时间】:2012-04-04 15:48:44
【问题描述】:

所以,我今天很无聊,决定搞乱 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. 注册类Pair

现在,如您所见,这不是很灵活,但确实有效,而且是双向的:

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. 类对象不能直接引用

那么,毕竟,您会在应用程序中推荐这种设计结构吗?以及为什么。

【问题讨论】:

  • 我对 C++ 了解的太少,无法很好地回答这个问题,但我想知道这个问题的答案是否取决于您正在处理的应用程序类型。被移植的现有 C++ 游戏可能会发现这比简单的实用程序应用程序更有用……而且经验丰富的 C++ 程序员可能比高度面向 Objective-C 的人更喜欢它。
  • 投票重新开放。我理解这可能“没有建设性”,但这是一个社区网站。我知道你是一名版主,但看到你是唯一一个希望关闭此内容的人。
  • 我不是版主,我已标记此问题以供审核。这是题外话。
  • 这是一个建设性的、切题的问题,对 Mac OS X 和 iOS 开发人员很有帮助。这是该市场中的开发人员经常面临的一个问题,他们正在利用数百(数千?)各种可用的 C++ 引擎中的任何一种。当然,这个例子本身对于最后的优点/缺点/特定问题来说是多余的,但是 ObjC 和 C++ 之间的通用桥接是推荐的模式吗? 的实际问题非常重要价值的具体问题!
  • 我还想指出,您在此处获得的相同运算符重载可以使用围绕 Objective-C 对象的 C++ 包装器进行复制。然后,提供到id 的隐式转换。我不支持这种技术,但指出它可能是解决该特定问题的更简单的方法。

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


【解决方案1】:

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

没有。

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

在“现实世界付费项目”使用的上下文中,问题在于您正在有效地创建一个相对通用的桥,然后必须在两端有特定的桥来与典型的 C++ 库或典型的 Objective- C API/库。换句话说,您有效地创建了一个从两个现有运行时的合并派生的新运行时。

而且,正如您在缺点中指出的那样,您几乎必须在要引入此模式的每个 C++ 类之上触摸、包装、修改和/或调试 shim。

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

【讨论】:

    最近更新 更多