理解 Xcode 编译系统

Vadim Bulavin· 2019-09-02
本文来自 iOSTips ,作者 Vadim Bulavin

捕获.PNG

题图:by 戴铭 极客时间专栏 《iOS 高手课》

作者:Vadim Bulavin; 译者:老峰

何 iOS 源代码在设备上运行之前都需要编译器的一系列处理,这个过程通常由 Xcode Build System 完成。在这篇文章中,我将介绍 Xcode Build System 的每一个部分。

为何要学编译知识

说说 OCLint 、SwiftLint 实现原理是怎样的?
如何编写 Clang 插件?
Obfuscator-LLVM 在 iOS 中如何实现混淆加固?
iOS 中 Bitcode 到底是如何优化 IPA 包的?
如果以上问题你都可以说个大概,请忽略本文,如果你对以上问题一知半解,但又很感兴趣,那么学习并掌握编译原理相关知识的过程中,你便会自己找到答案,好了接下来我将大概的介绍 iOS 开发中需要了解的编译流程。

语言处理系统

语言处理系统让自己输出一个可执行程序的一组任意源语言编写的指令。它允许程序员使用高级语言而不是写作机器代码大大减少了编程的复杂性。

我们日常使用的语言处理系统 iOS 或 macOS 开发 叫做 Xcode Build System。

Xcode Build System

Xcode 构建系统的主要目的是协调执行各种构建任务,最终将产生一个可执行程序。

Xcode 通过运行一系列编译器工具集将 iOS 源码按一定的顺序编译链接生成可执行文件,而无需你手动操作,关心编译链接背后复杂的过程。

大部分的语言处理系统,包括 Xcode Build Sytem,包括以下 5 个部分:

  • Preprocessor

  • Compiler

  • Assembler

  • Linker

  • Loader

这五部分组合起来是下面的流程图:

捕获.PNG

让我们仔细看看每一个步骤。

Preprocessing

预处理步骤的目的是将你的程序做一些处理然后可提供给编译器。它会处理宏定义、发现依赖关系、解决预处理器指令。

Xcode 解决依赖关系通过底层 llbuild 构建系统。它是开源的,你可以在 Github swift-llbuild 页面了解更多信息。

Compiler

编译器是一个程序,将一种语言的源程序用另一种语言映射到一个语义上等价的目标程序。换句话说,它转换Swift、objective - C和C / C++ 代码到机器码。

Xcode 使用两个不同的编译器:一个用于 Swift ,另一个用于Objective - C, Objective - C + +和 C / C++文件。

clang 是苹果官方的 C 语言编译器。它是开源在:swift-clang。

swiftc 是 Xcode 用来编译和运行 Swift 源代码的 Swift 编译器。

编译器工作流程如下:

捕获.PNG

编译器由两个主要部分:前端和后端。

前端负责词法分析,语法分析,生成中间代码;它还创建并管理符号表,收集关于源程序的信息。

符号表存储名称的变量,函数,类,你的名字,每个符号映射到特定的数据。

编译原理之美

Swift 编译器,中间语言表示名为 Swift Intermediate Language(SIL)。它是用于进一步分析和优化的代码。不可能直接从 Swift 中间语言生成机器代码,因此 SIL 经历了一系列转变到 LLVM 中间表示。

后端以中间代码作为输入,进行行架构无关的代码优化,接着针对不同架构生成不同的汇编代码。

Assembler

Assembler 翻译开发者可读的汇编代码为可重定位的机器码,最终生成包含数据和代码的 Mach-O 文件。

机器代码是一种数字语言,表示一组指令,可以直接由 CPU 执行。它被是可重定位的,因为无论目标文件的地址空间在哪,它将执行的指令相对地址。

Mach-O 文件是一种特殊的 iOS 和 MacOS 文件格式,操作系统用它来描述对象文件、可执行文件和库。它是一串字节组合形成的有意义的程序块,将运行在 ARM 处理器上或英特尔处理器。 

Linker

链接器将各种对象文件和库链接合并为一个可以在 iOS 或 macOS 系统上运行的 Mach-O 可执行文件。链接器主要有两种文件作为输入,包括这些对象文件的汇编程序和库的几种类型(.dylib, .tbd 和 .a)。

链接器的作用,就是完成变量、函数符号和其地址绑定这样的任务。例如,如果在代码中使用 printf , 链接器链接这个符号和 libc 库 printf 函数实现的地方。通常在编译阶段通过创建符号表来解决不同对象文件和库的引用。

Loader

最后,加载程序是操作系统的一部分,将一个程序加载到内存中,并运行执行它。加载程序负责分配运行程序内存空间和初始化寄存器所需的初始状态。

总结

作为 iOS 和 macOS 开发者我们主要使用 Xcode Build System 编译构建我们的应用程序。它的主要组件是:预处理、编译器、汇编器、连接器和加载程序。Xcode 使用不同的编译器(swiftc 和 clang)编译 Swift 和 Objective-C。

对于初学者和经验丰富的开发人员来说学习掌握 编译原理基础知识都颇有益处,这里有一张文学老师《编译原理之美》的关于编译知识结构体系的思维导图,可以拿来对每个知识点系统学习

image.png