在iOS日常开发过程中常常遇到APP启动过于缓慢,那我们如何去优化解决它呢?要想解决这个问题首先我们需要了解APP启动过程中做了什么东西,在了解整个过程之后我们在启动的每一步寻找方案。
虚拟内存
早期计算机数据访问,是访问物理内存,直接访问物理地址。但是这种方案有两个明显的缺点:
- 数据不安全;
- 内存不足;
针对数据不安全问题Apple提出了ASLR技术。ASLR的概念:(Address Space Layout Randomization )地址空间配置随机加载
,是一种针对缓冲区溢出
的安全保护技术
,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的的一种技术。其目的的通过利用随机方式配置数据地址空间
,使某些敏感数据配置到一个恶意程序无法事先获知的地址,令攻击者难以进行攻击。由于ASLR的存在,导致可执行文件和动态链接库在虚拟内存中的加载地址每次启动都不固定
,所以需要在编译时来修复镜像中的资源指针,来指向正确的地址。即正确的内存地址 = ASLR地址 + 偏移值
。
针对内存不足问题Apple在进程和物理内存之间增加一个中间层
,这个中间层就是所谓的虚拟内存
,主要用于解决当多个进程同时存在时,对物理内存的管理。提高了CPU的利用率,使多个进程可以同时、按需加载。所以虚拟内存其本质就是一张虚拟地址和物理地址对应关系的映射。虚拟内存具备以下特点:
1、 每个进程都有一个独立的虚拟内存
,其地址都是从0开始
,大小是4G固定的,每个虚拟内存又会划分为一个一个的页
(页的大小在iOS中是16K,其他的是4K
),每次加载都是以页为单位加载的,进程间是无法互相访问的,保证了进程间数据的安全性。
2、当CPU需要访问数据时,首先是访问虚拟内存,然后通过虚拟内存去寻址,即可以理解为在表中找对应的物理地址,然后对相应的物理地址进行访问
3、如果在访问时,虚拟地址的内容未加载到物理内存,会发生缺页异常
(Pagefault),将当前进程阻塞掉,此时需要先将数据载入到物理内存,然后再寻址,进行读取。这样就避免了内存浪费
4、一个进程中,只有部分功能是活跃的,所以只需要将进程中活跃的部分放入物理内存,避免物理内存的浪费
APP启动
APP的启动分冷启动和热启动
冷启动
:当手机内存中不包含APP任何数据的时候,第一次启动为冷启动。重启手机打开应用可达到冷启动效果。
热启动
:冷启动意外
我们研究的启动优化,其实是针对冷启动。这个过程分为两个阶段:
-
pre-main
,也称为dyld加载过程,此过程系统会将可执行文件加载到内存,并执行链接和加载过程。 - 从main函数开始,到didFinishLaunching加载结束,直至第一个页面加载完成。
pre-main加载优化
通过配置项目scheme添加环境变量DYLD_PRINT_STATISTICS
控制台可以打印出pre-main加载过程各个阶段耗时情况
控制台打印出的结果如下
下面我们探讨下各阶段加载的意义
dylib loading
:此过程主要加载动态库rebase
:偏移修正,可执行文件内部所有的方法、函数调用,都有一个地址,这个地址是其实是一个偏移地址。一旦在运行时刻(即运行到内存中),每次系统都会随机分配一个ASLR(地址空间布局随机化)地址值,那么函数的实际地址为ASLR地址+便宜地址binding
:符号绑定,绑定是给符号赋值的过程ObjC setup
:加载OC对象过程initializer
:加载load方法和构造函数总结:
1、项目中尽量少的使用动态库,超过6个动态库时尽量合并
2、避免创建不必要的OC类
3、避免在load方法里面处理不必要的业务
4、尽量少使用C++的构造函数
main函数以后加载优化
在Appdelegate 里面didFinishLaunching方法里面通常我们会放些数据库初始化、环境配置、主题配置、第三方配置、设置根试图控制器等业务。
1、要想提高启动速度我们在此阶段需要优化此阶段业务代码,非必须的业务,不调用或延时调用。
2、使用多线程,将不影响启动过程的业务处理放在子线程。初始化流程,使用懒加载方式。
3、页面的构建使用纯代码,少使用故事版。
网友评论