iOS性能(二) 启动时间优化

冷启动 与 热启动
  • 热启动:如果你刚刚启动过App,这时候App的启动所需要的数据仍然在缓存中,再次启动的时候称为热启动。通常情况下热启动能帮助提升启动速度,但有时也可能会出现app卡死手动退出进程后重新打开仍然是卡死状态。
  • 冷启动:如果是比较长时间没有启动过app或者设备刚刚重启,这种情况下启动App,就被称为冷启动。
查看启动时间
  • 最佳速度:400ms,因为不添加任何同步任务从图标被点击到显示Launch Screen,然后Launch Screen消失这段时间就是400ms。如果app启动时间接近这个数值,那证明app的启动任务已经优化到最佳。
  • 最慢速度:不可以大于20s,否则会被系统杀掉。

配置Xcode环境变量在日志中打印启动时间:
打开工程 -> Edit Scheme -> Run -> Environment Variables
根据需要添加DYLD_PRINT_STATISTICSDYLD_PRINT_STATISTICS_DETAILS环境变量,1表示Yes,开启这个功能。

iOS性能(二) 启动时间优化_第1张图片
屏幕快照 2019-05-27 下午3.13.41.png

Total pre-main time: 433.19 milliseconds (100.0%)
         dylib loading time: 341.79 milliseconds (78.8%)
        rebase/binding time:  14.18 milliseconds (3.2%)
            ObjC setup time:  35.27 milliseconds (8.1%)
           initializer time:  41.79 milliseconds (9.6%)
           slowest intializers :
             libSystem.B.dylib :   3.40 milliseconds (0.7%)
    libMainThreadChecker.dylib :  19.68 milliseconds (4.5%)
  libViewDebuggerSupport.dylib :   8.75 milliseconds (2.0%)
优化启动

以main函数作为分水岭,启动时间其实包括了两部分:main函数之前和main函数到第一个界面的viewDidAppear:。
所以,优化也是从两个方面进行的,优化效果主要来自于后者,因为绝大多数App的瓶颈在自己的代码里。而对于pre-main的优化能做的无非是减少不必要的动态库引用、多个库合并成一个,从上面的打印数据也可以看出,主要耗时是在dylib loading,消耗78.8%的时间。

Main函数之后

从main函数开始执行,到第一个界面显示,期间一般做以下任务:

  • 执行AppDelegate的代理方法,主要是didFinishLaunchingWithOptions
  • 初始化Window,初始化基础的ViewController结构(一般是UINavigationController+UITabViewController+多个UIViewController)
  • 获取数据(Local DB/Network),展示给用户。

优化:

  1. 延迟初始化和加载不必要的UIViewController和View。

比方说UITabViewController有四个Item,在启动的时候尽量只初始化首页的页面,其它Item页面先用空VC占位。而且首页的内容中不必要的内容也可以先不初始化,做成懒加载形式,在用户确实需要查看和使用时再初始化。

  1. 对于确实需要启动时使用但又比较耗时的事物放倒后台处理,如果涉及到UI则在处理完成后把刷新任务放回主线程。

日志功能,日志往往涉及到DB操作;
文件读取,比如读取本地存储的省份城市区县文件和图片处理;
大量的计算,比如图片处理、比较大的json数据转Model;

  1. 能延迟初始化的尽量延迟初始化

三方SDK初始化,比如Crash统计、 像分享之类的,可以等到第一次调用再出初始化。

Main函数之前

Main函数之前是iOS系统的工作,所以这部分的优化往往更具有通用性。
Pre-Main包含以下工作:

- dylib loading time: 341.79 milliseconds (78.8%)
- rebase/binding time:  14.18 milliseconds (3.2%)
- ObjC setup time:  35.27 milliseconds (8.1%)
- initializer time:  41.79 milliseconds (9.6%)
- slowest intializers :
- libSystem.B.dylib :   3.40 milliseconds (0.7%)
- libMainThreadChecker.dylib :  19.68 milliseconds (4.5%)
- libViewDebuggerSupport.dylib :   8.75 milliseconds (2.0%)

优化:

  1. loading dylib:启动的第一步是加载动态库,加载系统的动态库使很快的,因为可以缓存,而加载内嵌的动态库速度较慢。所以,提高这一步的效率的关键是:减少动态库的数量。
  • 合并动态库,比如公司内部由私有Pod建立了如下动态库:XXTableView, XXHUD, XXLabel,强烈建议合并成一个XXUIKit来提高加载速度。
  1. rebase/binding & ObjC Runtime setup:Rebase和Binding都是为了解决指针引用的问题。对于Objective C开发来说,主要的时间消耗在Class/Method的符号加载上,所以常见的优化方案是:
  • 减少__DATA段中的指针数量。
  • 合并Category和功能类似的类。比如:UIView+Frame,UIView+AutoLayout…合并为一个
  • 删除无用的方法和类。
  • 多用Swift Structs,因为Swfit Structs是静态分发的。可以参考Swift进阶之内存模型和方法调度
  1. Initializers
  • 用initialize替代load。不少同学喜欢用method-swizzling来实现AOP去做日志统计等内容,强烈建议改为在initialize进行初始化。
    load在程序启动的时候就会调用,而且必须阻塞等着所有类的load方法都执行完;initialize在类首次使用的时候调用。

  • 减少__atribute__((constructor))的使用(__attribute__((constructor))用是在main函数之前,执行一个函数,便于我们做一些准备工作)。


参考资料

深入理解iOS App的启动过程

你可能感兴趣的:(iOS性能(二) 启动时间优化)