不确定如何导出Objective-C类。架构i386的未定义符号

时间:2014-05-24 20:45:30

标签: objective-c linker clang

我正在尝试在OSX上对GTK +做一些工作而且我遇到了一些麻烦,因为,老实说,我并不熟悉Objective-C。我有足够的编程经验,我很快就掌握了基本语法,我可以在文档中查找我需要的东西。但是我遇到的问题与链接库和将类暴露给我正在链接的程序有关。

GTK +是一个C库,但OSX后端包含几个Objective-C类。它们不作为公共API公开,它们仅在内部使用;但是对于我正在研究的事情,我想尝试公开揭露这些类。

供参考,以下是其中一个类:

https://github.com/GNOME/gtk/blob/gtk-2-24/gdk/quartz/GdkQuartzWindow.h   https://github.com/GNOME/gtk/blob/gtk-2-24/gdk/quartz/GdkQuartzWindow.c

我更改了构建系统,以便安装头文件。我有另一个源文件(在gtk +构建系统之外),它可以执行以下操作:

GdkQuartzWindow *win = [[GdkQuartzWindow alloc] /* other messages... */ ];

当我尝试编译时,我收到链接器错误:

Undefined symbols for architecture i386:
  ".objc_class_name_GdkQuartzWindow", referenced from:
  pointer-to-literal-objc-class-name in main-a8d029.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我不太熟悉Objective-C编译器/链接器如何工作,并且无法理解我如何(或者如果可以)解决这个问题。

当我运行nm libgdk-quartz-2.0.dylib | grep GdkQuartzWindow时,我在这个课程上看到很多消息:

00057120 t -[GdkQuartzWindow beginManualMove]
000575c0 t -[GdkQuartzWindow beginManualResize]
00056c50 t -[GdkQuartzWindow canBecomeKeyWindow]
... (many more) ...
000b2030 s .objc_class_name_GdkQuartzWindow

在该列表的底部,您会看到链接器抱怨的内容不存在。但是,如果我使用nm -g代替nm,则不会显示任何内容,因此无法正确导出某些内容。

对于普通C符号,构建过程会构建包含

的别名文件

Apple's docs给我的印象是,当我的目标是i386(它目前是)时,这应该不是问题。他们说:

  

在32位OS X项目中,这些可见性控件仅适用于代码的C或C ++子集。它们不适用于Objective-C类和方法。 Objective-C类和消息名称由Objective-C运行时绑定,而不是由链接器绑定,因此可见性的概念不适用于它们。没有机制可以从该库的客户端隐藏动态库中定义的Objective-C类或方法。

但是,我收到一条错误,说我有未定义的符号,所以我的错误似乎与此相矛盾。如果我更改此行以便它分配NSWindow,那么一切正常;所以它正确导入AppKit的东西。看起来我在尝试公开类及其方法时做错了。

有人可以指出我正确的方向吗?

3 个答案:

答案 0 :(得分:1)

我建议您在链接到GTK +时确保使用-ObjC标志。您可以在"其他链接器标志"下添加它。在目标构建设置中。如果这不起作用,您可以尝试添加-all_load链接器标志。

这将影响库的加载方式并确保识别所有符号。这在链接包含Objective C类的库时实际上很有用,因为链接器会进行一些优化(即,当静态库定义类别但不直接使用它时),但它可能对您的情况有所帮助同样。

See this获得解释。

答案 1 :(得分:1)

作为临时解决方法,您可以通过调用NSClassFromString来替换代码中类符号的引用,并将类名作为参数传递。例如:

GdkQuartzWindow *win = [[NSClassFromString(@"GdkQuartzWindow") alloc] /* other messages... */ ];

这有长期的可维护性问题,因为它取决于类的名称保持不变。我怀疑Apple doc对于当前的编译器可能已经过时或者错误。也许检查一下gdk / quartz / Makefile.in中替换$(GDK_HIDDEN_VISIBILITY_CFLAGS)的值是否确实是您平台上的空字符串,如果没有,那么更改它可能是一个更可行的长期解决方案,满足您的需求。

答案 2 :(得分:1)

在带有混合C / C ++代码和ObjectiveC包装器的库中,我遇到了类似的问题。在这种情况下,我使用handleSort(el) { const compareValues = (key) => { return function innerSort(a, b) { if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) { // property doesn't exist on either object return 0; } const varA = typeof a[key] === "string" ? a[key].toUpperCase() : a[key]; const varB = typeof b[key] === "string" ? b[key].toUpperCase() : b[key]; let comparison = 0; if (varA > varB) { comparison = 1; } else if (varA < varB) { comparison = -1; } return comparison; }; }; const sorted = this.state.players.sort(compareValues(el)); this.setState({ players: sorted }); } gcc / clang标志编译了整个库。这具有隐藏所有ObjectiveC类符号的直接​​效果。一种不那么了解/使用的技术是用可见性属性来装饰类。这广泛用于C / C ++,但也适用于ObjectiveC声明的类型。首先,创建一个-fvisibility=hidden宏,该宏将使OBJC_MYLIBRARY_API文件中的编译单元可见:

libdefs.h

然后,在标头中声明带有公共API宏的Objective C类:

#pragma once

#if defined(OBJC_MYLIBRARY_SHARED) || !defined(OBJC_MYLIBRARY_STATIC)

#ifdef OBJC_MYLIBRARY_EXPORT
    #define OBJC_MYLIBRARY_API __attribute__ ((visibility ("default")))
#else
    #define OBJC_MYLIBRARY_IMPORT
    #define OBJC_MYLIBRARY_API
#endif

#else
    #define OBJC_MYLIBRARY_API
    #ifndef OBJC_MYLIBRARY_EXPORT
        #define OBJC_MYLIBRARY_IMPORT
    #endif
#endif

然后在实现中仅确保将导出定义OBJC_MYLIBRARY_MACRO的符号:

#pragma once

#import "cboclibdefs.h"

OBJC_MYLIBRARY_API
@interface MyClass : NSObject
- (id)init;
@end

这样,#define OBJC_CODEBINDER_EXPORT #import "MyClass.h" @implementation MyClass - (id)init { return [super init]; } @end 符号将在生成的共享库中可用。