我正在尝试在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的东西。看起来我在尝试公开类及其方法时做错了。
有人可以指出我正确的方向吗?
答案 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
符号将在生成的共享库中可用。