6

我有一个小型 C++ 应用程序,我导入了 Objective-C 类。它作为 Objective-C++ 文件 .mm 工作,但任何包含可能最终包含一些 Objective-C 头文件的头文件的 C++ 文件都必须重命名为 .mm 扩展名以用于正确的 GCC 驱动程序。

有没有办法为 Objective-C 类编写一个纯 C++ 包装器,或者我可以以某种方式将 Objective-C 对象分离出来并单独链接它们?也许即使 Objective-C 类变成了一个小型库,我也可以在编译时静态重新链接?

问题是这段代码是跨平台的,在通常不使用Objective-C的系统(即不是Mac)上编译起来更加困难。即使预处理器命令限制了任何在 Windows 或 Linux 上的 Objective-C 代码的实现,原始代码仍然具有 .mm 扩展名,并且 GCC 仍然将代码视为 Objective-C++。

4

2 回答 2

3

通常,您只需使用 C++ 类包装您的 Objective-C 类,例如使用不透明指针并将对 C++ 方法的调用转发到 Objective-C 方法。

这样,您的可移植 C++ 源代码就不必看到任何 Objective-C 包含的内容,理想情况下,您只需为不同平台上的包装器交换实现文件。

例子:

// c++ header:
class Wrapper {
    struct Opaque;
    Opaque* opaque;
    // ...
public:
    void f();
};

// Objective-C++ source on Mac:
struct Wrapper::Opaque {
    id contained;
    // ...
};

void Wrapper::f() {
    [opaque->contained f];
}

// ...
于 2010-02-09T05:41:37.463 回答
2

是的,如果您知道一些技巧,这两种方式都很容易实现:

1)“id”类型实际上是在纯 C 标头中定义的。因此,您可以执行以下操作:

在您的标题中:

#include <objc/objc.h>

class MyWindow
{
public:
    MyWindow();
    ~MyWindow();
protected:
    id        mCocoaWindow;
};

在您的实施中(.mm):

#include "MyWindow.h"
#include <Cocoa/Cocoa.h>

MyWindow::MyWindow()
{
    mCocoaWindow = [[NSWindow alloc] init];
}

MyWindow::~MyWindow()
{
    [mCocoaWindow release];
    mCocoaWindow = nil;
}

2) 当源文件包含两者之一但不是 ObjC++ 时,可以使用两个预处理器常量来排除 C++/ObjC 特定代码:

#if __OBJC__
// ObjC code goes here.
#endif /* __OBJC__*/

#if __cplusplus
// C++ code goes here.
#endif

请注意,您不能只使用#ifdef 添加/删除 ivars 或虚拟方法,这将创建两个具有不同内存布局的类,并使您的应用程序以非常奇怪的方式崩溃。

3)您可以使用指向结构的指针而不声明其内容:

在您的标题中:

@interface MyCppObjectWrapper : NSObject
{
    struct MyCppObjectWrapperIVars    *ivars;    // This is straight ObjC, no ++.
}

@end

在您的实现文件 (.mm) 中:

struct MyCppObjectWrapperIVars
{
    std::string    myCppString1;
    std::string    myCppString2;
    std::string    myCppString3;
};

@implementation MyCppObjectWrapper

-(id)  init
{
    if(( self = [super init] ))
    {
        ivars = new MyCppObjectWrapperIVars;
    }

    return self;
}

-(void) dealloc
{
    delete ivars;
    ivars = NULL;

    [super dealloc];
}

@end

这将使您的标头简单的标准 C 或 ObjC,而您的实现文件获取所有 ivars 的构造函数/析构函数调用,而无需创建/删除每个作为堆上的对象。

这只是 Mac 方面的事情,但这意味着您可以将 ObjC 内容保留在您的标头之外,或者至少在从 C++ 可移植性层的 Mac 实现的跨平台客户端文件中使用它时使其编译。

于 2010-02-14T23:27:05.773 回答