iOS启动优化

2023-07-22  本文已影响0人  Slaser

希望总结项目中的启动优化,详细学习iOS启动流程

1. 想要对App进行启动优化需要优先了解App的启动时间,和个个启动过程中的时间占比(主要讲解Pre-main之前的过程)

截屏2023-07-22 22.18.59.png 截屏2023-07-22 22.20.51.png

2. 了解了方法后我们可以通过打印看到我们每个过程消耗的时间,此处有几个关键指标

rebase(偏移修正):任何一个app生成的二进制文件,在二进制文件内部所有的方法、函数调用,都有一个地址,这个地址是在当前二进制文件中的偏移地址。一旦在运行时刻(即运行到内存中),每次系统都会随机分配一个ASLR(Address Space Layout Randomization,地址空间布局随机化)地址值(是一个安全机制,会分配一个随机的数值,插入在二进制文件的开头),例如,二进制文件中有一个 test方法,偏移值是0x0001,而随机分配的ASLR是0x1f00,如果想访问test方法,其内存地址(即真实地址)变为 ASLR+偏移值 = 运行时确定的内存地址(即0x1f00+0x0001 = 0x1f01)

dyld调用的objc_init方法,这个是runtime的初始化方法,在这个方法里面主要的操作就是加载类(对需要的class和category进行注册),objc_init方法通过内部的_dyld_objc_notify_register向dyld注册了一个通知事件,当有新的image(程序中对应实例可简称为image,如程序可执行文件macho,Framework,bundle等)加载到内存的时候,就会触发load_images方法,这个方法里面就是加载对应image里面的类,并调用load方法(在下一阶段initializer),如果有继承的类,那么会先调用父类的load方法,然后调用子类的,但是在load里面不能调用[super load]。最后才是调用category的load方法。总之,所有的load都会被调用到(注意:子类的initialize方法会覆盖父类,不同于load方法)

3. 介绍几个概念

1.Mach-O文件
Apple出品的操作系统的可执行文件格式几乎都是mach-o,iOS当然也不例外。
mach-o可以大致的分为三部分:

image.png

2.Virtual Memory
虚拟内存是建立在物理内存和进程之间的中间层。在iOS上,当内存不足的时候,会尝试释放那些只读的Page,因为只读的Page在下次被访问的时候,可以再从磁盘读取。如果没有可用内存,会通知在后台的App(也就是在这个时候收到了memory warning),如果在这之后仍然没有可用内存,则会杀死在后台的App。

3.Page fault
在应用执行的时候,它被分配的逻辑地址空间都是可以访问的,当应用访问一个逻辑Page,而在对应的物理内存中并不存在的时候,这时候就发生了一次Page fault。当Page fault发生的时候,会中断当前的程序,在物理内存中寻找一个可用的Page,然后从磁盘中读取数据到物理内存,接着继续执行当前程序。

4.改善APP的启动

-在 dylib loading的过程中,会去装载app使用的动态库,而每一个动态库有它自己的依赖关系,所以会消耗时间去查找和读取,Apple官方建议尽量少的使用自定义的动态库,或者考虑合并多个动态库,其中一个建议是当大于6个的时候,则需要考虑合并它们。简单的举个例子比如使用cocoapods管理的多个自定义的UI组件可以合并成一个自己的UIKIT,同时也建议动态库转静态库

-减少+load的方法使用,+load方法中尽量少做耗时操作,+load中代码延迟到 main 之后子线程处理或者首页显示之后;改为 initialize 中执行,针对 initialize 中处理需要注意的是分类 initialize 会覆盖主类 initialize 以及有子类后 initialize 执行多次的问题,需要使用 dispatch_once 来保证代码只执行一次;

-二进制重排要实现符号的重排,一是需要我们收集整个启动链路上的方法和函数等符号,二是需要生成对应的 order 文件来配置 ld 中的 Order File 属性。当工程在编译的时候,Xcode 会读取这个 order 文件,在链接过程中会根据这个文件中的符号顺序来生成对应的 MachO。一般业界中收集符号的方案有两种:
1.Hook objc_msgSend,只能拿到 OC 以及 swift @objc dynamic 的符号;
2.Clang 插桩,能完美拿到 OC、C/C++、Swift、Block 的符号;
第二种方法实现成本较高
采用第一种方法在编译完成后通过验证 LinkMap 文件中 #Symbols: 部分符号顺序是否和 order 文件中的符号顺序一致来确定是否配置成功即可

上一篇 下一篇

猜你喜欢

热点阅读