从一个静态库导出`OBJC_CLASS`作为另一个静态库的一部分

时间:2014-08-12 22:22:25

标签: ios objective-c static-libraries

我想创建一个静态库(实际上是一个框架,但我知道如何做这个部分),它从另一个静态库中捆绑代码。但是,原始库中的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文件夹。
    • LibBinclude目录拖到顶层。
    • LibA从第一个项目的include目录拖到顶层。
    • 验证{2}链接二进制文件阶段中是否显示LibA
    • 在向导生成的任何类的任何方法中(例如-[MasterViewController awakeFromNib]),添加(void)[[LibB alloc] init]
    • 在您刚编辑的.m文件的顶部,添加#import "LibB.h"
    • 构建

这是上面承诺的LibB.h

#import <Foundation/Foundation.h>
#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的符号将导出为未定义。

我可以从LibA引用方法,C函数和结构,全局变量等。甚至是Foundation对象上的类别(只要我做类别 - 虚拟技巧)。它只是类和元类对象,我无法弄清楚如何导出。

以下是我试图解决的问题:

  • 关闭死代码剥离(在所有三个项目中)。
  • 添加-ObjC作为额外的链接器标志(在所有项目中)。 (这对静态库没有任何意义,它所做的就是给你一个警告错误,告诉你这个,但每个人都向我推荐。)
  • 创建“导出的符号文件”(适用于LibB)。 (这也只对动态库有意义。)
  • ${PROJECT_DIR}/libLibA.a作为“其他链接标记”(针对LibB)传递,而不是将libLibA添加为框架(如果-lLibA的处理方式与{{1}不同}})。

我曾尝试过的,我仍然认为可能是在正确的道路上,但我不确定:

  • 尝试找出Xcode中没有相应设置的相应libLibA.a选项。 (如果需要,我可以将它包装在Makefile中,或者Xcode自定义构建步骤。)
  • 启用“执行单对象预链接”,然后将libtool添加到“预链接库”。我得到关于重复符号的警告然后成功,但是空${PROJECT_DIR}/libLibA.a,所以显然我还需要做其他事情。我已经在OS X上用.dylibs和动态框架完成了这个,而且我没有其他任何东西需要在那里做......但是从来没有使用静态库。

我了解的解决方法(如果没有真正的解决方案,我会使用其中一种方法):

  • 要求任何想要使用libLibB.a的人也必须将LibB添加到他们的项目中。特别是我们提供的预先构建的LibA副本。
  • LibA分发为要包含在项目中的源代码,而不是静态的lib和头文件。
  • 手动LibB arlibLibA.a,然后是LibB.o,就像1999年一样(虽然文档说工作,但似乎到)。

(对于我的简单测试项目来说,这些都不是太糟糕,但在现实生活中,这不是一个开源项目,LibA实际上是来自3个不同项目的80个不同的库,而且一些LibA代码构建了胖armv7 / armv7s(这意味着ranlib无法正常工作......),我们计划将模拟器和本机构建一起进行唇形化,并用它们构建一个框架,所有这些都是使问题更加严重。

2 个答案:

答案 0 :(得分:3)

我想我可能用单对象prelink解决了它(基本上这意味着它会建立一个巨大的目标文件ld -r,然后将其传递给libtool),尽管我还是不确定,我不喜欢这个解决方案。所以,我会发布我所得到的答案,但希望其他人能得到更好的答案。

要使单对象预链接起作用,您需要(在LibB中):

  • libLibA.a添加为框架。
  • 确保 Not 出现在L​​ink Binary With Libraries构建阶段。
  • 将“死代码剥离”设置为否。
  • 将“Do not Dead-Strip Inits and Terms”设置为是。
  • 将“执行单个对象预链接”设置为“是”。
  • 将“预链接库”设置为${PROJECT_DIR}/libLibA.a
  • 将“保留私有外部符号”设置为“是”。

(第二步是我之前做错了...)

不幸的是,这似乎完全打破了依赖规则,因此即使没有任何更改,每个构建都会重新编译目标中的每个.m(和.pch)。

除了那种烦恼之外,这似乎对AppC和我的真实项目都很有用。

AppC不需要“保留私有外部符号”;我的真实项目呢。我相信这是因为其中一个第三方库明确地ld -r使用空-exported_symbols_list来“将所有符号转换为private_extern。否则,类对象不会结束但是,我并不是100%肯定我理解这个。

答案 1 :(得分:0)

将其添加到Other Linker Flags似乎可行

-force_load $(CONFIGURATION_BUILD_DIR)/libLibA.a