iOS 之编译

himg

Dependency

术语声明

在查询编译原理的过程中, 我被网上的那些不规范用语彻底给整蒙了, 首先这里对机器语言和汇编语言称呼做下总结:

  • 机器指令 = 机器语言 = 机器码 = 机器代码 = 101010101010
  • 汇编指令 = 汇编语言 = mov ax,bx

为什么需要编译

计算机的核心是 CPU, CPU 中有上亿个晶体管, 运行的时候, 每个晶体管会根据电流的 关闭流通 来确认两种状态, 也就是我们说的 01.

为了对计算机发送指令, 人们发明了汇编语言, 这种语言使用了人类容易理解的字母组合来表示指令, 但是计算机是理解不了这种语言的,
因此还需要通过特定的编译器将汇编语言转换为 CPU 能理解的机器语言 (二进制)

写代码时我们使用的都是高级语言 (c / c++ / java / oc / swift 等), CPU 是不认识这些语言的, 编译的过程就是将高级语言转换为 CPU
可以识别的二进制. 在 iOS 开发中, Xcode 调用 LLVM 来完成编译过程, 将 Swift 语言经历 frontend -> optimizer -> backend
转换成机器可以识别的二进制指令. 这整个过程如下图所示:

himg

在此过程中, 语言经历了: 高级语言 -> 汇编语言 -> 机器语言 (二进制). 通过机器语言可以反编译为汇编语言, 通过汇编语言也可以反编译出高级语言,
不过很难, 因为有可能两个不同的高级语言命令产生的汇编语言是相同的

iOS 项目编译过程简介

我们的项目是一个 target, 一个编译目标, 它拥有自己的文件和编译规则, 在我们的项目中可以存在多个子项目, 这在编译的时候就导致了使用了 Cocoapods
或者拥有多个 target 的项目会先编译依赖库. 这些库都和我们的项目编译流程一致.

  1. 写入辅助文件: 将项目的文件结构对应表, 将要执行的脚本, 项目依赖库的文件结构对应表写成文件, 方便后面使用; 并且创建一个 .app 包,
    后面编译后的文件都会被放入包中
  2. 运行预设脚本: Cocoapods 会预设一些脚本, 当然你也可以自己预设一些脚本来运行. 这些脚本都在 Build Phases 中可以看到
  3. 编译文件: 针对每一个文件进行编译, 生成可执行文件 Mach-O, 这过程 LLVM 的完整流程, 前端, 优化器, 后端
  4. 链接文件: 将项目中的多个可执行文件合并成一个文件
  5. 拷贝资源文件: 将项目中的资源文件拷贝到目标包
  6. 编译 storyboard 文件: storyboard 文件也是会被编译的
  7. 链接 storyboard 文件: 将编译后的 storyboard 文件链接成一个文件
  8. 编译 Asset 文件: 我们的图片如果使用 Assets.xcassets 来管理图片, 那么这些图片将会被编译成机器码, 除了 icon 和 launchImage
  9. 运行 Cocoapods 脚本: 将在编译项目之前已经编译好的依赖库和相关资源拷贝到包中
  10. 生成 .app
  11. 将 Swift 标准库拷贝到包中
  12. 对包进行签名
  13. 完成打包

swiftc 与 clang 区别

不同于 OC 使用 clang 作为编译器前端, Swift 自定义了编译器前端 swiftc, 如下图所示.

himg

这里就体现出来 LLVM 三段式的好处了, 支持新语言只需实现编译器前端即可.

对比 clang, Swift 新增了对 SIL(Swift Intermediate Language) 的处理过程. SIL 是 Swift 引入的新的高级中间语言, 用以实现更高级别的优化.

Swift 编译流程

Swift 源码经过词法分析,语法分析和语义分析生成 AST。SILGen 获取 AST 后生成 SIL,此时的 SIL 称为 Raw SIL。在经过分析和优化,生成 Canonical
SIL。最后,IRGen 再将 Canonical SIL 转化为 LLVM IR 交给优化器和后端处理。

himg

Ref