App启动之Dyld在作什么

前言

这里主要剖析一下一个App从点击图标,到展示首页的整个过程。数据库

App是如何启动的

按顺序划分缓存

  • 加载可执行文件(读取Mach-O)
  • 加载动态库(Dylib)
  • Rebase & Bind
  • Objc
  • Initializers

--------- main() ---------安全

  • 执行AppDelegate的代理方法(如:didFinishLaunchingWithOptions)。
  • 根据业务注册SDK,获取数据库数据等。
  • 初始化Windows,初始化ViewController。

加载可执行文件(读取Mach-O)

Apple操做系统使用dyld加载可执行文件。数据结构

dyld全程为dynomic loader,做用是加载一个进程所须要的Image,在opensource-apple能够找到它的开源代码。架构

加载动态库(Dylib)

dyld读取完Mach-O的Header和Load Commands后,就会找到可执行文件的依赖动态库。接着dyld会将所依赖的动态库加载到内存中。这是一个递归的过程,依赖的动态库可能还会依赖别的动态库,因此dyld会递归每一个动态库,直至全部的依赖库都被加载完毕。app

加载后的动态库会被缓存到dyld shared cache中,提升读取效率。dom

简单的说下Mach-O,简单的能够分为三个部分,Header,Load Commands,Segment Data。函数

Header中包含的是可执行文件的CPU架构,Load Commands的数量和占用空间。布局

Load Commands中包含的是Segment的Header与内存分布,以及依赖动态库的版本和Path等。优化

Segment Data就是Segment汇编代码的实现,每段Segment的内存占用大小都是分页页数的整数倍。

iOS-dyld-2019-03-17-3

Rebase & Bind

这两个过程合在一块儿说,是由于他们之间的工做是相互补充的。

Apple为了解决应用安全,用到了ASLR(Address space layout randomization 地址空间布局随机化)和Code Sign。

App被启动后,会被映射到虚拟内存中,这样App在这个空间中就有了一个起始地址,但这个起始地址是固定的。ASLR能使这个起始地址随机化,这项技术能够防止攻击者经过初始地址+偏移量的方法找到函数的内存地址。

Code Sign就是签名,在进行加密的时候,会对每个Page(这里指的是Segment Data)都进行加密,当dyld进行加载的时候,会对每个Page都进行独立的验证。

Mach-O中采用了PIC(Position Independent Code 地址无关代码),大当咱们在调用函数时,会在__Data段中创建一个指向该函数的指针,经过这个指针来间接调用。

Mach-O中有不少符号,有些指向当前Mach-O的(咱们为App编写的代码),有些指向其余DyLib(依赖的动态库)。

Rebase的做用是从新修正指向当前Mach-O指针的指向,由于上面提到的ASLR将地址随机化,起始地址不在是固定的,从新修复后,App才能正常运行。

Bind的做用是从新修复外部指针的指向,这个过程会根据字符串匹配的方式来查找符号表,比起Rebase会略慢(这里fishhook的实现基础,它在dyld绑定C库的时候进行了hook)。

iOS-dyld-2019-03-17-2

Objc

由于Objective C的动态特性,因此在Main函数执行以前,须要把类信息注册到一个全局Table中。同时,Category的方法也会被注册到对应类中,Category中的同名方法实现,会根据编译顺序,被最后一个编译的Category实现所覆盖。同时还会作Selector的惟一性检测。

Initializers

这个阶段是包含必要的初始化。

  • +load
  • C/C++静态初始化对象和标记有__attribute__(constructor)的方法

这里区分下+load方法与+Initialize方法,前者是在类加载时调用的,后者是在类第一次收到message以前调用的。

main()方法以后的事

这里就不作展开了,都是咱们亲手写的代码。

Dyld 3

以上咱们介绍了dyld2的加载方式,在2017WWDC,Apple推出了Dyld3。

Dyld2是从程序开始时才开始执行的,而Dyld3则将Dyld2的一些过程进行了分解。

iOS-dyld-2019-03-17-1

Dyld3分为out-of-process,和in-process。

out-process会作:

  • 分析Mach-O Headers
  • 分析以来的动态库
  • 查找须要的Rebase和Bind的符号
  • 将上面的分析结果写入缓存。

in-process会作:

  • 读取缓存的分析结果
  • 验证分析结果
  • 加载Mach-O文件
  • Rebase&Bind
  • Initializers

使用了Dyld3后,App的启动速度会进一步提升

启动阶段的优化建议

  • 减小动态库的数量,推荐使用系统库。
  • 减小类和方法的数量。
  • 减小初始化函数。
  • 尽可能使用Swift。
    • Swift没有初始化器。
    • Swift不容许特定类型的未对齐数据结构。
    • Swift代码更精简。

想要了解Dyld的同窗,能够看看这篇文章App 启动流程以及优化 WWDC 2017

参考资料

深刻理解iOS App的启动过程

App 启动流程以及优化 WWDC 2017

相关文章
相关标签/搜索