从一个静态库导出`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文件夹。
    • LibBinclude目录LibB顶层。
    • LibA从第一个项目的include目录LibA顶层。
    • validationLibA是否出现在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作为“其他链接器标志”(对于LibBlibLibA ,而不是将libLibA作为框架添加(如果-lLibA的处理方式与libLibA.a不同)。

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

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

我知道的解决方法(如果没有真正的解决方案,我将使用其中一个):

  • 要求任何想要使用LibB也必须将LibA添加到他们的项目中。 特别是我们提供的LibA副本。
  • LibB分发为要包含在项目中的源,而不是静态的lib和头。
  • 手动ar libLibA.aLibB.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