APP项目优化--启动速度优化篇

  我们所开发的项目,随着线上功能逐渐稳定,导致项目启动速度越来越慢,而这又是用户对我们的项目给第一印象,所以启动速度变得尤为重要,如果启动速度缓慢,会造成比较严重的用户流失,所以,对启动速度的优化,将会成为我们后期开发工作中不可或缺的一部分。

 基础概念

  冷启动:是指启动并没有进程在系统里,需要系统新创建一个进程供APP使用的启动情况

  热启动:和冷启动对应,是APP的进程在系统里,用户重新启动进入APP的过程,如果把APP进程杀掉,然后立即重启,也属于热启动因为进程的缓存依然存在

  其实,从用户可感知的维度去看呢,就是用户在手机桌面,点击APP图标,到APP启动图完全消失,首页第一帧渲染完成的过程。所以,本文我们就只展开说APP冷启动的流程和优化。

  启动流程

  总得来说APP启动主要包括三个阶段:

  1、main()函数执行之前

  2、main()函数执行之后

  3、首屏渲染完成后

  关于 APP 启动时间,主要由 pre-main 和 main 之后的时间组成,即总的启动时间 = main 之前加载的时间 + main 之后加载的时间。启动时间越接近 400ms 越好,并且最好控制在 20s 以内,不然系统会以为 APP 进入一个死循环,应用进程将会被系统强制杀除。

  下面我们继续分别说一说这三个阶段系统都做了什么事情。

 main()函数执行前

  在main()函数执行前,系统主要会做下面几件事:

  1. 加载可执行文件(APP的.o文件的集合),就是Mach-o文件,这个在后面会详细介绍
  2. 加载动态链接库,进行rebase指针调整和bind符号绑定
  3. 运行时的初始化工作,包括类的注册,category注册,selector唯一性的检查等
  4. 执行+load()方法,attribute((constructor)) 修饰的函数的调用,创建 C++ 静态全局变量
  pre-main的加载时间,可以通过添加环境变量DYLD_PRINT_STATISTICS的方式打印出来  

 

  运行的时候就可以打印出详细的pre-main的时间 

  

  可以看到main()方法之前的时间由dylib loading ,rebse/binding,ObjC setup和initializer四个耗时的部分组成。

  所以,相对应这个阶段,我们可做的优化工作如下:

  • 减少动态库加载。每个库本身都有依赖,系统会递归的加载所有依赖的动态库,所以苹果公司建议,尽量使用更少的动态库
  • 减少无用的类、分类和方法
  • +load()方法中的内容如非必要,可以放到首屏渲染完成之后再执行,或者放在+initialize() 方法中执行。因为一个+load()方法会带来4毫秒的消耗
  • 控制C++ 静态全局变量的数量

 main()函数执行后

  main()函数执行后,是指从main()函数开始,到appDelegate 的 didFinishLaunchingWithOptions 方法里首屏渲染相关方法执行完成的过程。

  这个过程主要包括了:

  1. 日志、统计
  2. 配置APP运行需要的环境
  3. 第三方SDK初始化

  代码中,各种各样的初始化工作,全部都放在这个阶段去执行了,导致渲染缓慢。这里我们能做的优化工作是,从功能上梳理出来,到底哪些才是首屏渲染必要的初始化功能,哪些是APP启动必要的初始化功能,其他只需要在对应模块功能使用前才需要初始化的工作,分别放在他们对应的合适的位置。

  除此之外,使用 instrument 可以帮助我们进行分析didFinishLaunchingWithOptions中的耗时操作。

 首屏渲染完成后

  到这个阶段,用户已经看到我们APP的首页,这个阶段,所做的事情是其他业务模块的一些初始化工作,也就是appDelegate 的 didFinishLaunchingWithOptions 方法作用域结束的位置。

  所以这个阶段的优化优先级比较低,但是还是要注意,那些会阻塞主线程的操作,以防影响用户后面的操作。

 

 优化工作

  动态库加载方面

  因为主要的动态库都是系统的动态库,而系统本身对其都有相应的优化处理,所以我们能做的只有去掉无用的系统动态库,或者一步到位直接删除掉Link Binary With Libraries中的所有系统动态库,改为自动link系统动态库

 

  方法级别的优化

  完成了功能级别的优化后,APP的启动速度应该已经有了一定程度的缩短,下面我们再继续从方法级别去做优化。

  删除无用代码

  当我们的项目日渐壮大,业务线交错的时候,也许已经有了很多无用的冗余代码在里面,我们可以使用AppCode分析检测项目代码。

  检测完就需要手动移除无用代码了,但是注意检测结果并不十分准确,比如可能存在一些runtime映射的方法,也会被检测到,所以还是要人工再检查一遍,以防误删有用代码。

  抽象重复代码

  1、在iOS代码中可能会为同一个类写很多分类方法,由于参与开发同学较多,可能会导致方法重复,但是实际上运行起来只能有一个分类的方法被调用,这取决于哪个分类后被加载,然而编译的二进制代码中,两个方法应该是都存在的,这不仅会增加app体积,也会增加启动时间,所以应该杜绝这样的重复问题;

  2、有很多地方可能是名字不同,但是函数的功能相同,这个不容易被发现,需要大家在写代码的过程中注意;

  3、又或者两个函数名字比较接近,里面有很多相似的代码,这种情况下可以进行相同的代码的提取。

 

posted @ 2020-10-29 13:43  Mr·Xu  阅读(518)  评论(0编辑  收藏  举报