iOS编译与app启动

iOS编译

当一个xcode工程build之后一般会执行如下几个步骤:

  • 预处理
  • 语法和语义分析
  • 生成代码和优化
  • 汇编
  • 链接

iOS编译采用Clang作为编译器前端,LLVM作为编译器后端。流程如下

编译过程.png

Clang的任务:预处理、词法分析、语法分析、语义分析、静态分析、生成中间代码。
预处理:以#开头的代码预处理。包括引入的头文件和自定义宏。
词法分析:每一个.m源文件的声明和定义从string转化为特殊的标记流。
语法分析:将标记流解析成一颗抽象语法树( abstract syntax tree-AST)。
静态分析:包含类型检查和其他检查。
中间代码生成:生成LLVM代码。

iOS编译与app启动_第1张图片
Clang过程.png

LLVM的任务:将代码进行优化并产生汇编代码。
汇编器:将可读的汇编代码转换为机器代码,最终创建一个目标对象.o文件。
链接器的任务:把目标文件和库相连,最终输出可运行文件:a.out。

Mach-O

Mach-O是针对不同运行时可执行文件的类型。
文件类型:

  • Exectuable: 应用的主要二进制
  • Dylib: 动态链接库
  • Bundle: 不能被链接的Dylib,只能在运行时使用dlopen()加载

Mach-O镜像文件
所有Mach-O(可使用MachOView工具查看)都包含:__TEXT, __DATA, __LINKEDIT:

  • __TEXT 包含Mach header, 被执行的代码和只读常量。
  • __DATA 包含全局变量,静态变量。可读写。
  • __LINKEDIT 包含加载程序的元数据,比如函数的名称和地址。

app启动

当用户点击一个app,app从启动到打开第一个页面的时间 t = t1 + t2,其中t1 = 系统dylib和自身app可执行文件(app中所有.o文件的集合)的加载,t2为main函数到appdelegate中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法执行结束这段时间。

app启动后,先加载可执行文件,再使用dyld(动态链接器)动态递归加载dylib(系统的framework,oc runtime的libobjc,系统级别的libSystem)。由于dylib是共享的,所以可以减少app包的体积。

dylib加载过程

  1. load dylibs image
  2. Rebase image
  3. Bind image
  4. Objc setup
  5. initializers

Rebase/Bind是用来修复image中的资源指针,使其指向正确的地址(因为iamge是加载在虚拟内存中(ASLR))

Objc setup工作

  • 注册Objc类
  • 把category定义插入方法列表
  • 保证每一个selector唯一

initializers
前面三步是静态调整,修改__DATA segment内容。这里开始动态调整,开始在堆栈中写入内容。

main()之前的加载时间

它可以通过以下方式来显示。

iOS编译与app启动_第2张图片
launch time.png

结果如下:


iOS编译与app启动_第3张图片
pre-main time.png

main()调用之后的加载时间

  1. 准备阶段,主要是图片的解码
  2. 布局阶段,-(void)layoutSubViews()
  3. 绘制阶段,-(void)drawRect:(CGRect)rect
  4. 启动阶段必要服务的启动、必要数据的创建和读取。

优化启动时间

  • 内嵌的dylib尽可能少,或者合并起来。
  • Rebase/Binding减少__DATA中需要修正的指针。 对于oc来说减少 class, selector, category 这些元数据的数量,对与c++来说,减少虚函数数量。swift结构体需要修正的比较少。
  • 将不必须在+load中做的事延迟到+ initialize中。
  • 不使用xib,直接用代码加载首页视图。
  • release版不要用NSLog输出。
  • 启动时的网络请求尽可能异步。

参考链接

趣探 Mach-O:文件格式分析
优化 App 的启动时间
Mach-O 可执行文件
编译器

你可能感兴趣的:(iOS编译与app启动)