这篇文章是对于自己学习 App编译过程的一个总结
学习的相关文章
iOS App的编译过程
iOS 编译过程的原理和应用
iOS 中的静态库与动态库,区别、制作和使用
本文的大纲
为何要了解
- 自己的兴趣,解开一些疑惑;
- 有利于解决一些编译过程中出现的问题;
- 对于优化App有很大的帮助。
什么是编译
为什么要编译
我们通常所使用的编程语言编写的代码,计算机的CPU(只能执行二进制代码)无法执行,所以要编译。
为什么要编译
什么是编译
利用编译程序将高级语言所编写的代码变为CPU可执行的代码的过程就叫编译。
App的编译过程
我分别用OC和Swift语言创建两个Demo,通过BulidLog看一下整个编译过程:
Build的得到的.app也可以通过查看包内容,直观的看到得到的文件
根据图中和参考的文章总结一下 iOS 项目编译过程:
- 写入辅助文件:将项目的文件结构对应表、将要执行的脚本、项目依赖库的文件结构对应表写成文件,方便后面使用;
- 创建App架构:为填充后面步骤编译得到的文件等;
- 运行预设脚本:Cocoapods 会预设一些脚本,当然你也可以自己预设一些脚本来运行。这些脚本都在 Build Phases 中可以看到;
- 编译.m文件:这个过程是由LLVM完成的,编译成一个可执行文件(Mach-0);
- 链接编译后得到的文件;
- 拷贝项目中的资源,比如 图片资源;
- 编译 storyboard
- 编译 asset文件:图片如果使用 Assets.xcassets 来管理图片,那么这些图片将会被编译成机器码,除了 icon 和 launchImage;
- 处理info.plist ;
- 执行CocoaPod脚本;
- 拷贝swift标准包;
- 构建.app并签名;
- 完成打包。
以上,就是iOS项目大体的编译过程,下面说一下解释代码用到的LLVM 。
什么是LLVM
The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines. The name "LLVM" itself is not an acronym; it is the full name of the project.
LLVM项目是模块化、可重用的编译器和工具链技术的集合。尽管名为LLVM,但它与传统的虚拟机几乎没有关系。“LLVM”这个名字本身并不是首字母缩略词;这是项目的全称。
LLVM在编译过程中分为了三个模块: 前端、中间优化器和后端 。(跟web前端,service后端没有任何关系)。
LLVM各个模块的作用:
- 前端:对目标语言代码进行语法分析,语义分析,生成中间代码。在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行;
- 中间优化器:对中间代码进行优化,去除冗余代码,这个过程会进行BitCode的生成,链接期优化等;
- 后端:先进行与机器无关的代码优化,生成汇编语言,在生成汇编语言之后会再进次进行与机器相关的代码优化,最后将各个文件的机器代码链接。
在iOS中,OC和Swift两种语言的编译在前端是有差别的:OC中使用的编译器前端为:Clang,在Swift中使用的编译器前端为swift自己编写的,这个我们可以在BuildLog里面具体看到。
OC的文件编译处理过程:
Objective-C 的文件中,只有 .m 文件会被编译 .h 文件只是一个暴露外部接口的头文件,它的作用是为被编译的文件中的代码做简单的共享,并且因为OC没有private和public的用法,用.h和.m来实现private和public。
- 预处理 :处理一些预处理指令( 比如#define、#ifdef,#else,#endif等)并将预处理后的代码进行符号化处理,以便下一步进行词法分析和语义分析;
- 词法分析和语义分析:
<1> 将符号化的代码抽象为语法树(abstract syntax tree – AST);
<2> 静态分析:对语法树进行遍历分析,包括类型检查、实现检查(某个类是否存在某个方法)、变量使用,还会有一些复杂的检查,例如在 Objective-C 中,给某一个对象发送消息(调用某个方法),检查这个对象的类是否声明这个方法(但并不会去检查这个方法是否实现,这个错误是在运行时进行检查的),如果有什么错误就会进行提示。因此可见,Xcode 对 clang 做了非常深度的集成,在编写代码的过程中它就会使用 clang 来对代码进行分析,并及时对代码错误进行提示。- 生成 LLVM 代码(也就是中间代码LLVM Intermediate Representation LLVM IR),并将代码递交给优化器,这也是LLVM前端 Clang的最后一步;
- 优化:将一些不合适且消耗内存的代码进行优化;
- 生成目标文件:这之后就是由LLVM后端完成了,将优化过的代码根据不同架构的 CPU 转化生成汇编代码,再生成对应的可执行文件,这样对应的 CPU 就可以执行了;
- 生成可执行文件(Mach - 0)。
以上的文件编译流程在文章的开头链接里有详细步骤和说明。
Swift的文件编译处理过程:
总体而言,Swift编译器主要负责将Swift源代码转换为高效、可执行的机器代码。但是,Swift编译器前端还支持许多其他工具,包括与语法着色、代码完成和其他便利的IDE集成。本文件对Swift编译器的主要组件进行了高层描述:
- 解析:解析器是一个简单的递归解析器(在lib/Parse中实现),带有一个集成的、手工编码的lexer。解析器负责生成没有任何语义或类型信息的抽象语法树(AST),并对输入源的语法问题发出警告或错误。
- 语义分析:语义分析(在lib/Sema中实现)负责将解析后的AST转换为结构良好的、完全类型检查的AST形式,为源代码中的语义问题发出警告或错误。语义分析包括类型推断,如果成功,则表明从生成的经过类型检查的AST生成代码是安全的。
- Clang导入:Clang导入器(在lib/ClangImporter中实现)导入Clang模块,并将它们导出的C或Objective-C api映射到相应的Swift api中。产生的导入的ast可以通过语义分析来引用。
- SIL生成:Swift中间语言(SIL)是一种高级的、特定于Swift的中间语言,适用于Swift代码的进一步分析和优化。SIL生成阶段(在lib/SILGen中实现)将类型检查的AST降低为所谓的“原始”SIL。SIL的设计在doc /SIL.rst中进行了描述。
- SIL保证转换:SIL保证转换(在lib/SILOptimizer/Mandatory中实现)执行影响程序正确性的附加数据流诊断(例如使用未初始化的变量)。这些转换的最终结果是“规范的”SIL。
- SIL优化:SIL优化(在lib/Analysis、lib/ARC、lib/LoopTransforms和lib/Transforms中实现)对程序执行额外的高级特定于swift的优化,包括(例如)自动引用计数优化、去虚拟化和泛型专门化。
- LLVM IR生成:IR生成(在lib/IRGen中实现)降低SIL到LLVM IR,此时LLVM可以继续优化并生成机器代码。
生成LLVM IR 之后的步骤就跟OC一样了。