在面试比较常见的一个问题,做iOS这么多年了,能不能讲讲iOS的编译过程?这个过程中都有哪些产物?下面我们就来简单梳理一下

编译器

编译的第一步,肯定需要有一个编译器。它是用来完成高级语言到低级语言(机器码)的一个转换。

现代编译器主要工作流程: 现代编译器标准三阶段结构: 编译器前端(frontend)、优化器、编译器后端(backend)

iOS开发中我们目前采用的编译器是LLVM,曾经有用GCC(GNU Compiler Collection)。

LLVM

LLVM 项目是模块化和可重用的编译器和工具链技术的集合,它的命名最早源自于底层虚拟机器(Low Level Virtual Machine),后面随着项目的发展,LLVM决定放弃缩写的含义,直接打造成了一个品牌。“The name “LLVM” itself is not an acronym; it is the full name of the project.”

Clang是LLVM的主要子项目之一,它是C系列语言的编译器前端(C、C++、Objective-C、Objective-C++)。

Objective-C编译需要Clang与LLVM后端来完成。 Swift的编译则是通过Swift编译器前端与LLVM后端完成的。

Swift的编译器前端中swift和swiftc到底有什么区别呢?

swift 是 Swift 的交互式的编程环境 (REPL)。

swiftc 是 Swift 编译器。

swiftc 是 swift 的一个快捷方式

Swift的编译流程相对于Objective-C来说,中间多了SIL(Swift 中间语言)层,想了解更多的Swift编译过程可以看文末【Swift Compiler】

LLVM编译流程(Clang为例)

1、创建一个Command Line Tool项目(OC为例),编译其中的main.m文件

#import

int main(int argc, const char * argv[]) {

@autoreleasepool {

NSInteger a = 3;

NSInteger b = 4;

NSInteger c = MAX(a, b);

NSLog(@"the result = %ld", c);

}

return 0;

}

查看源文件编译所经历的阶段,在终端输入如下命令:

终端输入:clang -ccc-print-phases main.m

终端输出:

+- 0: input, "main.m", objective-c

+- 1: preprocessor, {0}, objective-c-cpp-output

+- 2: compiler, {1}, ir

+- 3: backend, {2}, assembler

+- 4: assembler, {3}, object

+- 5: linker, {4}, image

6: bind-arch, "x86_64", {5}, image

(1)预处理阶段

预处理阶段主要做以下事情:

导入头文件对宏定义进行替换处理#开头的预编译指令,如:#define、#pragma、#if等

查看预处理结果:

终端输入:clang -E main.m

终端输出:

# 1 "/Path/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h" 1 3

# 193 "/Path/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" 2 3

# 9 "main.m" 2

int main(int argc, const char * argv[]) {

@autoreleasepool {

NSInteger a = 3;

NSInteger b = 4;

NSInteger c = ({ __typeof__(a) __a0 = (a); __typeof__(b) __b0 = (b); (__a0 < __b0) ? __b0 : __a0; });

NSLog(@"the result = %ld", c);

}

return 0;

}

通过上面的终端输出结果我们发现:头文件已经被导入且宏定义也已经被成功替换了。

(2)词法分析阶段

将预处理完成后的代码转换成token流(有些书中也称token为词法单元),如+、=都可以称之为一个个token。

终端输入:clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m

终端输出:

......

......

identifier 'NSInteger' [StartOfLine] [LeadingSpace] Loc=

identifier 'a' [LeadingSpace] Loc=

equal '=' [LeadingSpace] Loc=

numeric_constant '3' [LeadingSpace] Loc=

semi ';' Loc=

identifier 'NSInteger' [StartOfLine] [LeadingSpace] Loc=

identifier 'b' [LeadingSpace] Loc=

equal '=' [LeadingSpace] Loc=

numeric_constant '4' [LeadingSpace] Loc=

semi ';' Loc=

identifier 'NSInteger' [StartOfLine] [LeadingSpace] Loc=

identifier 'c' [LeadingSpace] Loc=

equal '=' [LeadingSpace] Loc=

......

......

(3)语法分析阶段

验证语法的正确性,将所有节点组合成抽象语法树(AST)

clang -fmodules -fsyntax-only -Xclang -ast-dump main.m

其中校验语法的正确性是通过Static Analysis(静态分析)来完成

(4)CodeGen生成IR代码(中间代码生成)

CodeGen负责将语法树从上至下遍历,翻译成LLVM IR代码。LLVM IR即是编译器前端输出,也是编译器后端的输入,属于桥接性质的语言。

// 生成IR,这一步会产生.ll扩展名文件

clang -S -fobjc-arc -emit-llvm main.m -o main.ll

// 这块LLVM可以做一些优化工作,Xcode中可以设置优化级别(Optimization Level)

clang -O3 -S -fobjc-arc -emit-llvm main.m -o main.ll

关于Optimization Level相关设置项:

None [-O0]:不做优化-默认在Debug模式开启Fast [-O, O1]:优化编译需要更多时间,大型函数需要更多内存。Faster [-O2]:编译器执行几乎所有支持的优化,不涉及空间速度折衷,不执行循环展开、函数内联、寄存器重命名。与Fast设置项比,增加了编译时间和生成代码的性能Fastest [-O3]:开启由Faster设置指定的所有优化项,并打开函数内联和寄存器重命名选项。可能会使可执行文件变大Fastest, Smallest [-Os]:优化大小,开启除了增加代码大小之外的所有更快的优化,执行旨在减少代码大小的进一步优化。Release环境默认开启Fastest, Aggressive Optimizations [-Ofast]:启用Fastest的所有优化,也会启用可能会破坏严格标准合规性激进优化项。Smallest, Aggressive Size Optimizations [-Oz]:通过将重复代码模式隔离到编译器生成的函数中来进一步节省大小。

更详细的解释,请参照文末“Build Settings Reference”

如果开启bitcode,则会进一步优化生成中间码(Xcode 14不推荐使用bitcode)

// 产生.bc扩展名文件

clang -emit-llvm -c main.m -o main.bc

(5)生成汇编代码

// 产生.s扩展名文件

clang -S -fobjc-arc main.m -o main.s

(6)生成目标文件

// 产生.o扩展名文件

clang -fmodules -c main.m -o main.o

(7)生成可执行文件Mach-O(链接)

// Mach-O文件:

clang main.m -o main

测试运行可执行文件:

// 当前文件路径下,执行Mach-O文件

./main

编译流程示意图:

Xcode Build 全过程(Xcode 12.3)

关于编译速度优化思路.

到目前为止,整个iOS的编译流程基本上已经梳理清楚。

那么整个编译过程中都产生了哪些文件呢?你心中应该已有答案!

参考资料

LLVM官网Swift CompilerBuild Settings Reference深入剖析 iOS 编译 Clang / LLVM

完整优秀版请移步小专栏: iOS 编译一览

更多好文推荐,扫描下方的二维码,关注《iOS开发秘籍》公众号,点关注不迷路!

本文内容中部分来自网络,后续会持续更新完善。欢迎一起学习交流!

如需转载,请注明出处

iOS 编译一览

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: