从一个静态库导出`OBJC_CLASS`作为另一个静态库的一部分
我想创建一个静态库(实际上,一个框架,但我知道如何做这个部分),它包括另外一个静态库的代码。 但是,从原始库导出的OBJC_CLASS
最终为未定义的符号。
例如,在Xcode 5.1.1中(除非另有说明,否则在每个步骤使用默认设置/选项):
- 创建一个名为LibA的新“iOS Framework&Library Cocoa Touch Static Library”项目。
- 构建(对于模拟器或真实设备,无关紧要)。
- 创建另一个名为LibB的新“iOS Framework&Library Cocoa Touch Static Library”项目。
- 将
libLibA.a
从LibA产品拖到LibB项目树中的Frameworks文件夹。 - 将
LibA
从静态库旁边的include
目录拖到LibB项目树的顶层。 - 编辑
LibB.h
,如下所示。 - 构建(与以前相同的目标)。
- 将
- 创建一个名为AppC的新“iOS应用程序”(任何类型)项目。
- 将
libLibB.a
从LibB产品拖到AppC项目树中的Frameworks文件夹。 - 将
LibB
从include
目录LibB
顶层。 - 将
LibA
从第一个项目的include
目录LibA
顶层。 - validation
LibA
是否出现在Link Binary With Libraries阶段。 - 在向导生成的任何类的任何方法中(例如,
-[MasterViewController awakeFromNib]
),add(void)[[LibB alloc] init]
。 - 在刚刚编辑的
.m
文件的顶部,添加#import "LibB.h"
。 - 建立。
- 将
这是LibB.h
承诺的LibB.h
:
#import #import "LibA.h" @interface LibB: LibA @end
我收到以下错误:
Undefined symbols for architecture i386: "_OBJC_CLASS_$_LibA", referenced from: _OBJC_CLASS_$_LibB in libLibB.a(LibB.o) "_OBJC_METACLASS_$_LibA", referenced from: _OBJC_METACLASS_$_LibB in libLibB.a(LibB.o) ld: symbol(s) not found for architecture i386 clang: error: linker command failed with exit code 1 (use -v to see invocation)
查看文件,问题很明显:
$ nm -g libLibB.a U _OBJC_CLASS_$_LibA 0000031c S _OBJC_CLASS_$_LibB U _OBJC_METACLASS_$_LibA 00000308 S _OBJC_METACLASS_$_LibB U _OBJC_METACLASS_$_NSObject U __objc_empty_cache
_OBJC_CLASS_$_LibA
和_OBJC_METACLASS_$_LibA
将导出为undefined。
我可以从LibA引用方法,C函数和结构,全局变量等。 甚至基金会对象上的类别(只要我做类别 – 虚拟技巧)。 它只是类和元类对象,我无法弄清楚如何导出。
这是我试图解决的问题:
- 关闭死代码剥离(在所有三个项目中)。
- 添加
-ObjC
作为额外的链接器标志(在所有项目中)。 (这对于静态库来说没有任何意义,它所做的就是给你一个警告错误,告诉你这个,但每个人都向我建议。) - 创建“导出的符号文件”(对于
LibB
)。 (这也只对动态库有意义。) - 将
${PROJECT_DIR}/libLibA.a
作为“其他链接器标志”(对于LibB
)libLibA
,而不是将libLibA
作为框架添加(如果-lLibA
的处理方式与libLibA.a
不同)。
我试过的,我仍然认为可能是在正确的道路上,但我不确定:
- 尝试找出在Xcode中没有相应设置的适当的
libtool
选项。 (如果需要,我可以将它包装在Makefile中,或者Xcode自定义构建步骤。) - 启用“执行单对象预链接”,然后将
${PROJECT_DIR}/libLibA.a
添加到“预链接库”。 我得到关于重复符号然后成功的警告,但是有一个空的libLibB.a
,所以很明显我还需要做其他事情。 我已经在OS X上用.dylibs和动态框架完成了这个,而且我没有其他任何东西需要在那里做…但是从来没有使用静态库。
我知道的解决方法(如果没有真正的解决方案,我将使用其中一个):
- 要求任何想要使用
LibB
也必须将LibA
添加到他们的项目中。 特别是我们提供的LibA
副本。 - 将
LibB
分发为要包含在项目中的源,而不是静态的lib和头。 - 手动
ar
libLibA.a
和LibB.o
,然后像1999年的ranlib
(尽管文档说这不起作用 ,似乎)。
(对于我的简单测试项目来说,这些都不是太糟糕,但在现实生活中,这不是一个开源项目,LibA实际上是来自3个不同项目的80个不同的库,并且一些LibA代码构建了胖armv7 / armv7s(这意味着ar
不起作用……),我们计划通常将模拟器和本机构建以及框架中的框架组合在一起,所有这些都会使问题更加严重。
我想我可能用单对象prelink解决了它(基本上这意味着它做了一个ld -r
来构建一个巨大的目标文件,然后将它传递给libtool
),虽然我仍然不确定,我不爱解决方案。 所以,我会发布我所得到的答案,但希望其他人能得到更好的答案。
要使单对象LibB
起作用,您需要(在LibB
):
- 将
libLibA.a
添加为框架。 - 确保它没有出现在Link Binary With Libraries构建阶段。
- 将“死代码剥离”设置为否。
- 将“Do not Dead-Strip Inits and Terms”设置为Yes。
- 将“执行单个对象预链接”设置为“是”。
- 将“Prelink libraries”设置为
${PROJECT_DIR}/libLibA.a
- 将“保留私有外部符号”设置为“是”。
(第二步是我之前做错了…)
不幸的是,这似乎完全破坏了依赖规则,因此即使没有任何改变,每个构建都会重新编译每个.m(和.pch),它们都是目标的一部分。
除了这种烦恼,这似乎适用于AppC
和我的真实项目。
AppC
不需要“保留私有外部符号”; 我的真实项目呢。 我相信这是因为其中一个第三方库明确地使用空的-exported_symbols_list
执行ld -r
“将所有符号转换为private_extern
。否则,类对象不会以这种方式结束。但是,我不是100我确定理解这个。
将此添加到Other Linker Flags
似乎工作
-force_load $(CONFIGURATION_BUILD_DIR)/libLibA.a