[关闭]
@qidiandasheng 2022-08-23T09:34:16.000000Z 字数 4120 阅读 5506

Xcode的build setting(❎)

iOS调试&工具


symbols相关

symbols概念

一个典型的ELF可重定位目标文件包含下面几个节:
... ...
.symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量信息。一些程序员错误地认为必须通过-g选项来编译程序才能得到符号表信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的条目。
... ...
.debug:一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。只有以-g选项调用编译驱动程序时才会得到这张表。
... ...

为了构造可执行文件,链接器必须完成两个主要任务:

符号解析(symbol resolution)。目标文件定义和引用符号。符号解析的目的是将每个符号引用刚好和一个符号定义联系起来。
重定位(relocation)。编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。


Search Paths

这里主要的就是一个路径的问题,比如我们链接某个动态库或静态库到我们工程的时候,如果是使用的绝对路径,那给别人用的时候或者团队合作的时候就需要修改这个路径,会很麻烦,所有在Search Paths里常常会有这样一个设置:$(SRCROOT)${PODS_ROOT},当你写入进去的时候,就会发现他会自动变成当前工程或Pods所在的目录。

(User) Header Search Path

这里有两个很类似的选项,User Header Search PathsHeader Search Path,一般带Users的是表示用户自定义的头文件的搜索路径。

Header Search Paths 顾名思义就是用来存放 Project 中头文件的搜索根源,没有被add到项目里的头文件,可以通过配置Header Search Paths 来引入头文件,这样的好处可以不让project 包含的文件太多,便于管理。

浅显一点的区别是,编码时候通过 #include 引入头文件的方式有两种 <> 和 ""。<> 是只从 Header Search Paths 中搜索, 而 "" 则能从 Header Search PathsUser Header Search Paths 中搜索。换言之 ,假如你把 路径加到 User Header Search Paths 中,那么 你用 #include <file.h> 的方式去引入对应的头文件,就会报错。 如果加到 Header Search Paths, 就没有问题了。

具体一点的区别是,<> 是从系统目录空间 (对应 Header Search Paths)中搜索文件, "" 是从用户目录空间(对应 User Header Search Paths)中搜索文件。如果你把路径加到 User Header Search Paths 中,而 <> 无法从系统目录空间中找到新加的路径,从而报错。

所以如cocoapods这样安装第三方库的话,cocoapods会在Header Search Path写入对应库的路径,比如:"${PODS_ROOT}/Headers/Public/AFNetworking",而User Header Search Paths会是空的。

recursive或non-recursive

(User) Header Search Path这个里面的每一个路径后面都会有一个选项recursivenon-recursive。这个表示是否递归搜索头文件。

有时候我们在#import的时候编译器会没有代码补全头文件,只能我们手动输入头文件的全名,这样是很不方便的。

比如我们用cocoapods引入某个.framework库的时候,默认路径后面都为non-recursive,也就是不递归搜索头文件,也就不会有代码补全。而且引入也要#import "frameworkName/public.h"这样引入,告诉编译器是哪个framework下的头文件。

但是如果我们在User Header Search Paths下输入${PODS_ROOT}并设置为recursive,那编译器就会帮你找到所有Pods目录下的头文件,包括framework里的所有头文件。这样你在#import的时候就会有自动补全提示了,而且引入framework的头文件也只需要这样引入#import "public.h"但是这样带来的问题就是会浪费编译器的时间

Always Search User Paths

优先搜索User Header Search Paths路径下的文件,这种情况下如果有同名的头文件,那么User Header Search Paths就会覆盖Header Search Paths里的文件。

$(inherited)

Project 的 Building Settings 中得设置默认并不被 Targets 继承
只有 Targets 的设置加入了 $(inherited) 时才被继承,添加目录的时候写上 $(inherited) 就表示从 Frameworks 里面读取。


Linking

链接器

一个程序从简单易读的代码到可执行文件往往要经历以下步骤:

源代码 > 预处理器 > 编译器 > 汇编器 > 机器码 > 链接器 > 可执行文件

源文件经过一系列处理以后,会生成对应的.obj文件,然后一个项目必然会有许多.obj文件,并且这些文件之间会有各种各样的联系,例如函数调用。链接器做的事就是把这些目标文件和所用的一些库链接在一起形成一个完整的可执行文件。

Other Linker Flags

有时候我们在安装第三方Pod的时候,在运行时会出现selector not recognized这样的崩溃。那可能就是因为这个第三方Pod里面有分类,Objective-C的链接器并不会为每个方法建立符号表,而是仅仅为类建立了符号表。这样的话,如果静态库中定义了已存在的一个类的分类,链接器就会以为这个类已经存在,不会把分类里的方法加入到这个类的method list里面。这样的话,在最后的可执行文件中,就会缺少分类里的代码,这样函数调用就失败了。

所以我们需要在Other Linker Flags里加入需要链接的静态库和对应的参数,这里的参数主要有以下三个:

一般我们在使用cocoapods的安装第三方库时,cocoapods默认会生成静态库,所以cocoapods会把这些静态库加入到Other Linker Flags里面,cocoapods安装完后我们能看到Other Linker Flags里有$(inherited)-ObjC-|libName-framework换行 "framworkName"这些参数。每一个"framworkName"上面都会有一个-framework

其实我们把除$(inherited)外的都删了也不会有错,删了之后我们把鼠标悬浮在Other Linker Flags的参数上面,还是能看到跟之前一样的配置,只是双击点进去就没了。应该就是$(inherited)继承了上一层的配置,但是这个上一层在哪呢?

关于ios的ipa包的分析之link map 文件的分析

设置Write Link Map File为YES,就能在Products里的文件所在的目录找到了。
/Users/dasheng/Library/Developer/Xcode/DerivedData/dfc_v2-guuwzafnatopzhgxrftmngjzjcnj/Build/Intermediates/dfc_v2.build/Release-iphoneos/dfc_v2.build/dfc_v2-LinkMap-normal-armv7s.txt

User-Defined

可以通过一些配置来Hook一些编译的阶段。

https://stackoverflow.com/questions/28283500/figure-out-ld-command-arguments-from-xcode-run-script-build-phase

配置CompileC

xcode在编译每一个类时使用的是clang,可以在User-Defined里通过配置CCCXX来自定义clang。

配置Ld

xcode在最后的链接阶段生成目标文件会通过clang,可以在User-Defined里通过配置LDLDPLUSPLUS来自定义clang。

配置Libtool

xcode在生成每一个pod库对应的静态库时会使用libtool工具,可以在User-Defined里通过配置LIBTOOL来自定义libtool。


参考

Xcode中和symbols有关的几个设置
Build 过程
关于Xcode的Other Linker Flags

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注