iOS启动优化

3,002 阅读5分钟

一、启动流程

1.1、pre-main阶段

1、加载应用可执行文件

自身App的所有.o文件的集合

2、加载dyld动态链接器

dyld是一个专门用来加载动态链接库的库

3、dyld递归加载应用所有依赖的动态链接库dylib

动态链接库包括:iOS 中用到的所有系统 framework,加载OC runtime方法的libobjc,系统级别的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)。

1.2、main()阶段
  1. dyld调用main()
  2. 调用UIApplicationMain()
  3. 调用applicationWillFinishLaunching
  4. 调用didFinishLaunchingWithOptions
1.3、首屏渲染

首屏构建完成可浏览/可操作页面

二、查看启动时长

ios15之前,Xcode菜单Product-Scheme-edit Scheme...0-Run-Arguments-Environment Variables,添加 DYLD_PRINT_STATISTICS 环境变量,value为1,可以看到打印输出pre-main()之前的启动各项时长

Total pre-main time: 539.46 milliseconds (100.0%)//main()总的启动时间
         dylib loading time: 232.89 milliseconds (43.1%)//加载动态库占用
        rebase/binding time:  32.56 milliseconds (6.0%)//指针重定位占用
            ObjC setup time:  22.18 milliseconds (4.1%)//ObjC类初始化占用
           initializer time: 251.68 milliseconds (46.6%)//各种初始化占用
           slowest intializers :
             libSystem.B.dylib :   5.12 milliseconds (0.9%)//intializers初始化最耗时1
    libMainThreadChecker.dylib :  29.74 milliseconds (5.5%)//intializers初始化最耗时2
                      Template : 382.83 milliseconds (70.9%)//intializers初始化最耗时3

三、优化

pre-main

  • dylib loading: 加载动态库。这里的动态库指的是自己制作的动态库存,苹果提供的动态库存是在共享缓存里的,可以通过减少动态库的数量来优化这一部分所消耗的时间。苹果的建议是一个项目里自己制作的动态库数量不超过6个。
  • Objc setup: 注册Ojbc类,进行selector唯一性检测等。可以通过减少Ojbc类的数量,减少selector的数量来进行优化
  • rebase/binding: rebase指针修复,binding符号绑定。这一步优化手段和第2步一样。
  • initializer: 各种初始化的操作,比如执行objc的+load函数,C++的构造函数等,不要在+load函数里做一些耗时操作,或者把一引起操作延时的放在+initializer里面去执行
  • slowest intializers:在共享缓存里加载,都是苹果系统的动态库,经过了苹果的高效优化

main

  • 减少使用xib和storyboard

  • 删除NSLog打印,每次用NSLog方式打印会隐式的创建一个Calendar,因此需要删减启动时各业务方打的log,或者仅仅针对内测版输出log

  • 整理didFinishLaunchingWithOptions⽅法⾥⾯的业务逻辑,可以异步请求的异步请求,可

    以延时加载的延时加载。

四、二进制重排

ASLR

ASLR(address space layout randomization),地址空间布局随机化。是⼀种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测⽬的地址的难度,防⽌攻击者直接定位攻击代码位置,达到阻⽌溢出攻击的⽬的的⼀种技术。使得可执⾏⽂件和动态库在虚拟内存中的地址在每次启动都不固定。

Page Fault

由于虚拟内存的出现,进程不直接访问物理内存,这样安全性更⾼。为了提⾼效率和⽅便管理,对虚拟内存和物理内存进⾏分⻚(Page)管理。当进程访问⼀个虚拟内存⻚,⽽对应的物理内存却不存在的时候,会触发⼀个缺⻚中断,这个就叫Page Fault。

二进制重排

概念 :找到程序在启动时候需要调⽤的符号,然后修改编译参数完成⼆进制⽂件的重新排布。

LinkMap :是iOS编译过程的中间产物,记录了⼆进制⽂件的布局。可以在Xcode的Build Settings⾥开启Write Link Map File。

order⽂件 :编译器会按照order⽂件的内容,对⼆进制⽂件进⾏排列。可以在Xcode的BuildSettings⾥的Order File处设置。

⼆进制重排能够优化启动时间的原理 :App在执⾏的过程中会存在⼤量的Page Fault,⼀个Page Fault的耗时很少,但是当⼤量的Page Fault存在时,就会影响到代码的执⾏速度。同理,在App启动的时候,就可能会出现⼤量的Page Fault。⼆进制重排就是把在启动过程中需要使⽤到的符号,重新排列在⼀个或者⼏个Page⾥⾯,减少Page Fault的次数,从⽽达到减少启动时间的⽬的。

五、优化手段总结

  1. 移除不必要的动态库和不必要的类,使用fui(Find Unused Imports)可以扫描没有使用到的类
  2. 二进制重排优化
  3. 合并功能相似的类和扩展,由于Category的实现原理,和ObjC的动态绑定有很强的关系,实际上类的扩展是比较占用启动时间的。尽量可能合并一些扩展,并不是让你不使用扩展
  4. 压缩资源图片,图片小了,IO操作量小了,启动就快了
  5. 优化applicationWillFinishLaunching,需要在applicationWillFinishLaunching里处理的业务较多时,可以管理起这些任务将不需要马上在applicationWillFinishLaunching执行的代码延后执行
  6. 不使用xib,直接视用代码加载首页视图
  7. NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,如果文件太大的话一次能读取到内存中可能很耗时
  8. 删减启动时各业务方打的log