像C++,Objective C都是编译语言。编译语言在执行的时候,必须先通过编译器生成机器码,机器码可以直接在CPU上执行,所以执行效率较高。
像JavaScript,Python都是直译式语言。直译式语言不需要经过编译的过程,而是在执行的时候通过一个中间的解释器将代码解释为CPU可以执行的代码。所以,较编译语言来说,直译式语言效率低一些,但是编写的更灵活
Objective C采用Clang(swift采用swiftc)作为编译器前端,LLVM作为编译器后端。
编译器前端的任务是进行:词法分析,语法分析,语义分析,生成中间代码(intermediate representation )。在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。
编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化。iOS的编译过程,后端的处理如下
当你在XCode中,选择build的时候(快捷键command+B),会执行如下过程
编译信息写入辅助文件,创建编译后的文件架构(name.app)
处理文件打包信息,例如在debug环境下
Entitlements:
{
“application-identifier” = “app的bundleid”;
“aps-environment” = development;
}
执行CocoaPod编译前脚本
例如对于使用CocoaPod的工程会执行CheckPods Manifest.lock
编译各个.m文件,使用CompileC和clang命令。
CompileC ClassName.o ClassName.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
export LANG=en_US.US-ASCII
export PATH=“…”
clang -x objective-c -arch x86_64 -fmessage-length=0 -fobjc-arc… -Wno-missing-field-initializers … -DDEBUG=1 … -isysroot iPhoneSimulator10.1.sdk -fasm-blocks … -I 上文提到的文件 -F 所需要的Framework -iquote 所需要的Framework … -c ClassName.c -o ClassName.o
通过这个编译的命令,我们可以看到
clang是实际的编译命令
-x objective-c 指定了编译的语言
-arch x86_64制定了编译的架构,类似还有arm7等
-fobjc-arc 一些列-f开头的,指定了采用arc等信息。这个也就是为什么你可以对单独的一个.m文件采用非ARC编程。
-Wno-missing-field-initializers 一系列以-W开头的,指的是编译的警告选项,通过这些你可以定制化编译选项
-DDEBUG=1 一些列-D开头的,指的是预编译宏,通过这些宏可以实现条件编译
-iPhoneSimulator10.1.sdk 制定了编译采用的iOS SDK版本
-I 把编译信息写入指定的辅助文件
-F 链接所需要的Framework
-c ClassName.c 编译文件
-o ClassName.o 编译产物
例如,我们通过iTunes Store下载微信,然后获得ipa安装包,然后实际看看其安装包的内容。
通过XCode的Link Map File,我们可以窥探二进制文件中布局。 在XCode -> Build Settings -> 搜索map -> 开启Write Link Map File
![
Link Map File 直译为 链接映射文件,是 Xcode 生成可执行文件时一起生成的文本,用于记录链接相关信息。
可执行文件的路径
CPU架构
.o目标路径
方法符号
查看代码加载顺序
理解内存分段分区
Crash 时通过 Symbols 定位源码的机制
分析可执行文件中类或库体积,优化包体积
Xcode 在生成可执行文件的时候默认情况下不生成该文件。 在Xcode的配置中 Target -> Build Setting -> Linking 将Write Link Map File设置为YES来生成Link Map File,运行代码即可生成Link Map File
主要分为三个部分
a. text 段
这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
b. data 段
数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。
c. bss 段
bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
bss是英文Block Started by Symbol的简称。
d. 堆(heap)
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
e. 栈(stack)
栈又称堆栈,是用户存放程序临时创建的局部变量,
也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。
由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场。
从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
(3)Section 部分
Mach-O 文件中的虚拟地址最终会映射到物理地址上。这些地址被分成不同的Segement: TEXT段、DATA段、__LINKEDIT段。
__TEXT 包含 Mach header,被执行的代码和只读常量(如C 字符串),只读可执行(r-x)。
__DATA 包含全局变量,静态变量等,可读写(rw-)。
__LINKEDIT 包含了加载程序的元数据,比如函数的名称和地址,只读(r–)。
Segement 划分成了不同的 Section,不同的 Section 存储着不同的信息,下面是一些常用的 Section 的介绍。
二进制重排 就是要重新排列这些 方法符号的顺序 ,中心思想就是把启动用到的代码挪到前面的位置加载!