Other linker flags

CocoPod 是一个了不起的工具,很多团队也都通过它实现了项目的组件化。但在使用过程中不知道你是否也遇到过链接错误的问题。通常这都是因为 CocoaPod 在生成项目时自动在 Other Linker Flags 中添加 -ObjC 引起,那么 -ObjC 这个参数具体是做什么的呢,还有哪些参数呢?

-ObjC 的意义

-ObjC 参数的意义是告诉链接器,将静态库中所有实现了 Objective-C 或 Category 的成员(目标文件),链接到最终的可执行文件中。那这又是为什么呢?为什么需要给这么一个选项呢?在解决这个问题之前,先来回顾一下有关链接的知识。

静态语言的链接

像 C 这样的典型静态语言,所有“源文件”都会被编译为包含着可执行函数和静态数据的“目标文件”,之后在由“链接器”负责,将这些目标文件整合为一个可执行文件。

通常来说我们的工程中源文件不会只有一个,而且各个源文件之间还可能存在引用关系。例如: A.c 中用到了 B.c 中的一个名为 doSomething() 的函数。当编译器遇到这个函数时,它会将一个 “undefined symbol” 写入到生成的目标文件中,表示这个位置“缺少东西”。之后,当链接器遇到这个标记时,它就会知道这里有空缺需要填补。随着连接器的深入,它在B.o 中找到了doSomething() 的实现,这时它就会用这个实现的地址将 A.o 中的 “undefined symbol” 替换掉,从而将 A.o B.o 链接在一起。

UNIX 静态库是一个目标文件的集合,只有那些可以填补“undefined symbol”(空缺)的目标文件才会被整合到最终的可执行文件中,这样做也是为了减少最终可执行文件的体积。

动态语言的链接

在当下面试都要手撕“Runtime”的时代,大家对于 Objective-C 是一门动态语言,一定不会感到陌生。那么它跟静态语言有什么不同呢?其中一个最大的区别就是:直到方法被调用,否则在此之前无从得知这个方法的实现具体在什么位置。因此,Objective-C 的编译器也就没必要为方法设置“链接标识”了,但是类的链接标识依然存在。

举例来说,假设在源文件 A.m 有这样一条语句 [b doSomething] ,那么经过编译后,A.o 中会有一个用来指示“ b 空缺”的“undefined symbol”,但方法 doSomething 的“undefined symbol”却不会有。

Other linker flags

Objective-C 这种动态语言特性为我们带来了很多便利,但也存在一些弊端。例如,当我们的静态库中包含 category 时。根据前面介绍的动态语言链接特性,不难看出,category 的目标文件将不会被链接到可执行文件中,因为其它目标文件中都没有需要链接到位于 category 中的方法的“undefined symbol”。这样生成的可执行文件,在运行时就可能会出现 “selector not recognized” 的异常。

综上,我们也就不难理解 -ObjC 的意义了。那除此之外还有没有其它标识呢?

Linker flags list

答案是肯定的。因为我们只能让链接器覆盖大部分情况,一些边边角角的问题,还是需要人为干预。怎么干预呢,正是通过下面这些标识:

  • -all_load : 加载静态库中所有成员(简单暴力,但通常代价也不小)
  • -force_load <路径参数> : 加载指定路径静态库中的所有成员(这样目标就明确多了)
  • -arch <架构名>:加载指定架构的目标文件,例如:armv7,arm64,i386。
  • -framework <name[, suffix]>:为链接器指定 “name.framework/name”的framework检索地址。如果指定了后缀,那么将优先检索。例如:“name.framework/name_suffix”.

这里仅列举了部分常见的,欲了解更多,请在终端中输入 man ld 查阅 manpage。

最后

这个问题其实自己遇到很多次了,但总是看完之后,过后一段时间不用就又忘了。此外,国内中文环境越来越差,稍微有点价值的内容都被各个平台封闭在自己圈中,要不就是SEO污染,想通过搜索引擎检索到有意义的内容越来越难了。这也算是略近绵薄之力吧。

参见

Building Objective-C static libraries with categories
Other linker flags list