这里主要剖析一下一个App从点击图标,到展示首页的整个过程。数据库
按顺序划分缓存
--------- main() ---------安全
didFinishLaunchingWithOptions
)。Apple操做系统使用dyld加载可执行文件。数据结构
dyld全程为dynomic loader,做用是加载一个进程所须要的Image,在opensource-apple能够找到它的开源代码。架构
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的内存占用大小都是分页页数的整数倍。
这两个过程合在一块儿说,是由于他们之间的工做是相互补充的。
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)。
由于Objective C的动态特性,因此在Main函数执行以前,须要把类信息注册到一个全局Table中。同时,Category的方法也会被注册到对应类中,Category中的同名方法实现,会根据编译顺序,被最后一个编译的Category实现所覆盖。同时还会作Selector的惟一性检测。
这个阶段是包含必要的初始化。
这里区分下+load方法与+Initialize方法,前者是在类加载时调用的,后者是在类第一次收到message以前调用的。
这里就不作展开了,都是咱们亲手写的代码。
以上咱们介绍了dyld2的加载方式,在2017WWDC,Apple推出了Dyld3。
Dyld2是从程序开始时才开始执行的,而Dyld3则将Dyld2的一些过程进行了分解。
Dyld3分为out-of-process,和in-process。
out-process会作:
in-process会作:
使用了Dyld3后,App的启动速度会进一步提升
想要了解Dyld的同窗,能够看看这篇文章App 启动流程以及优化 WWDC 2017