使用 Objective-C++ 和 Objective-C 的链接器问题

Linker problem using Objective-C++ with Objective-C

目前我有一个 iOS 项目正在使用 Objective-C。出于性能原因,我实现了 Objective-C++ 代码,如下所示:

base11.mm

#include <string>
#include "base11.h"

static const std::string characters = "0123456789_";

const char* to_base11(long n) {
    // some c++ code here
}

long from_base11(const char* input) {
    // some c++ code here
}

base11.h

#ifndef base11_h
#define base11_h

#include <stdio.h>

const char* to_base11(long n);
long from_base11(const char* input);

#endif /* base11_h */

当然,我为 class 使用了 .mm 扩展,我只使用普通 C 函数参数和 return 类型。

然后我有一个 Objective-C class 将这些 C++ 函数包装在以下方法中:

Util.m

#include "base11.h"

+(NSString*) convertBase10ToBase11:(long) base10 {
    const char* base11 = to_base11(base10);
    return [NSString stringWithUTF8String:base11];
}

+(NSNumber*) convertBase11ToBase10:(NSString*) base11 {
    const char* input = [base11 UTF8String];
    return @(from_base11(input));
}

Util.h:

@interface Util : NSObject
    +(NSString*) convertBase10ToBase11:(long) base10;
    +(NSNumber*) convertBase11ToBase10:(NSString*) base11;
@end

然而,这会产生以下编译错误:

Undefined symbols for architecture x86_64:
  "_from_base11", referenced from:
      +[Util convertBase11ToBase10:] in Util.o
  "_to_base11", referenced from:
      +[Util convertBase10ToBase11:] in Util.o
ld: symbol(s) not found for architecture x86_64

然而,这很奇怪。我选择不对 Util.m class 使用 .mm 扩展,因为我不在那里使用任何 C++ 代码。另请注意,base11.h 头文件也未使用任何 C++ 代码。那么,为什么会出现此错误?按照这个post我的做法应该是正确的:

How to call a method on .mm file from a objective c class

这是函数符号名的问题。 C++ 允许具有相同名称和不同原型的函数。 因此,当使用 C++/Objective-C++ 编译的函数时,它会获得描述它原型的符号名称并使其独一无二。

所以你需要强制编译器使用C的符号名。 您可以通过在函数原型之前添加 extern "C" 来实现。 但是如果你在C和C++源文件中都使用这个头文件就不太方便了,因为C不知道它是什么extern "C".

幸运的是CoreFoundation已经解决了这个问题。你可以看看他们是如何宣布他们的 CFString.h, CFArray.h.

所以你只需要使用 CoreFoundation 的宏:CF_EXTERN_C_BEGINCF_EXTERN_C_END.

#ifndef base11_h
#define base11_h

#include <stdio.h>
#include <CoreFoundation/CFBase.h>

CF_IMPLICIT_BRIDGING_ENABLED
CF_EXTERN_C_BEGIN

const char* to_base11(long n);
long from_base11(const char* input);

CF_EXTERN_C_END
CF_IMPLICIT_BRIDGING_DISABLED

#endif /* base11_h */