iOS的启动优化

应用的启动优化

当我们参与到大型应用的时候 会遇到一些启动时间过长的情况 这时候就需要使用到相关的操作。
总结来说,main()方法调用前,启动过程大体分为如下步骤:
先是LLVM把项目翻译成IR文件然后到backend

  1. LLVM clang

  2. pre-main

  3. main

  4. binding – 符号绑定

  5. rebase – 指针修复 (iOS14.3引进ASLR‘地址空间随机化’解决了虚拟内存从0开始的问题,物理内存地址随机 – 内存不够用’通过写入物理内存,使用时再从物理内存中读取‘ )

  6. MMU 内存管理单元 – 翻译地址 – page(页)

    1、内核加载可执行文件

    2、load dylibs image (加载程序所需的动态库镜像文件)

    3、Rebase image / Bind image (由于ASLR(address space layout randomization)的存在,可执行文件和动态链接库在虚拟内 存中的加载地址每次启动都不固定,所以需要修复镜像中的资源指针)

    4、Objc setup (注册Objc类、将Category中的方法插入方法列表)

    5、initializers (调用Objc类的+load()方法、调用C++类的构造函数)

·针对APP启动时间的优化还是很有必要的。

   关于APP启动时间的分析和优化可以以main()为分界点,分为main()方法执行之前的加载时间(pre-main time)和main()之后的加载时间。那么,如何定量的测量这两个阶段具体的执行时间呢,下面先给出测量方法,看一下自己项目启动时间是否合理:

在Xcode中添加环境变量参数DYLD_PRINT_STATISTICS即可,这样运行APP时在控制台就会打印出pre-main花费的时间
如果想打印详细的pre-main中各个过程花费的时间,可以再添加一个DYLD_PRINT_STATISTICS_DETAILS参数pre-main time和total time的值不一样,其实下边的total time - debugger pause time就和上边的pre-main time大
(PS:因为iOS13之后就失效了,这两个属性所以使用时需要使用一个iOS13以下的模拟器)

如果你想打印dyld装载动态库的顺序,可以设置这个环境变量 DYLD_PRINT_LIBRARIES。
图:
在这里插入图片描述
在这里插入图片描述

请添加图片描述

main()方法调用之前启动过程的解析:

App开始启动后,系统内核(XNU)首先加载可执行文件(自身App的所有.o文件的集合),然后加载动态链接器dyld,dyld是一个专门用来加载动态链接库的库。 执行从dyld开始,dyld从可执行文件的依赖开始, 递归加载所有的依赖动态链接库。

(补充:用户点击图标之后,会发送一个系统调用 execve 到内核,内核创建进程。接着会把主二进制 mmap 进来,读取 load command 中的 LC_LOAD_DYLINKER,找到 dyld 的的路径。然后 mmap dyld 到虚拟内存,找到 dyld 的入口函数_dyld_start(最终调到dyld的_main函数),把 PC 寄存器设置成_dyld_start,接下来启动流程交给了 dyld。)

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

优化动作

1、减少动态库的引用,将项目中不使用的Framework及时删除,将Xcode配置中General -> Linked Frameworks and Libraries中使用不到的系统库不再引用。

2、合并动态库。

3、尽量不使用内嵌(embedded)的dylib,加载内嵌dylib性能开销较大。

4、清理项目中冗余的类、category。对于同一个类有多个category的,建议进行合并。

5、将不必须在+load方法中做的事情延迟到+initialize中。

6、尽量不要用C++虚函数(创建虚函数表有开销),不要在C++构造函数中做大量耗时操作。

查找项目中没有用到的类

下载工具fui ,link:https://github.com/dblock/fui

下载安装好工具以后
cd到工程目录下运行,$fui find ,然后就会打印出没有被使用的class

如何查找到没有被调用的函数

python FindSelectorsUnrefs.py -a {
    
    这里是工程中.app文件的地址在finder找到直接拖过来}

对应脚本FindSelectorsUnrefs.py可以在我上传的资源里面查找。

找到项目中没有被使用的图片资源

在我上传的资源文件中找到LSUnusedResources,的Mac应用跑起来然后把工程拖进去就可以了。

二进制重排

在这里插入图片描述

通过创建.order 文件 与项目进行绑定,使APP启动的时候能够按照我们制定的内容把我们需要的符号排在前面, 减少我们的程序启动过程中page fault的次数,从而减少启动时间。至于怎么才能知道要优化正确的顺训呢?

clang插桩

方法:
通过在viewcontroller页面中重写方法并打印出__fun__

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    
    

        if (!*guard) return;
            //可以根据函数找到对应符号的名称和地址。
    void *PC = __builtin_return_address(0);
    Dl_info info;
    dladdr(PC, &info);
    NSLog(@"\n dli_sname=%s",info.dli_sname);
}

系统会先在__sanitizer_cov_trace_pc_guard_init返回编译的文件个数,每次调用方法都会经过__sanitizer_cov_trace_pc_guard的回调方法,通过这里打印出来的先后顺序再到.order指定加载的顺序就可以搞掂二进制重排,通过把需要调用的类的方法优先加载好继而提高了APP的启动速度。

那么怎样系统才会在每个方法messagesend之前先执行上述方法呢 ?
在这里插入图片描述
如图所示先到buildSetting配置

猜你喜欢

转载自blog.csdn.net/lingjunjie/article/details/128386577