了解 iOS 启动优化

本文要是要针对启动优化相关概念和最佳实践的介绍。算法

什么是启动

首先来看一下启动类型都有哪几种,以及每种启动类型的特色。缓存

启动类型

共有三种启动类型,分别为:冷启动、热启动和从新启动,下面为它们的特色。markdown

  • 冷启动
    • 重启以后。
    • APP 未在 iPhone 的内存中。
    • APP 线程不存在。
  • 热启动
    • 最近被终止的。
    • APP 部分在内存中。
    • APP 线程不存在。
  • 从新启动
    • APP 被暂停(好比按下 home 键)。
    • 整个 APP 都在内存中。
    • APP 线程存在。

介绍完启动类型,下面来看一下启动共分为几个阶段以及每一个阶段的应该避免的作法。数据结构

启动的六大阶段

六大阶段分别为:系统接口初始化(System Interface)、Runtime 初始化、UIKit 初始化、Application 初始化、初始化第一帧、扩展(Extended)。app

先看下每一个阶段都具体作了哪些事情。异步

  • System Interface:初始化底层的系统接口、系统部分的工做花费固定的时间,也就是说该阶段的系统部分是不用咱们操心的。
  • Runtime Init:初始化语言的运行时环境、调用全部类的静态 load 方法。
  • UIKit Init:实例化 UIApplication 和 UIApplicationDelegate、开始事件处理和系统集成。
  • Application Init:调用 UIApplicationDelegate/UISceneDelegate 生命周期的回调,好比:application:willFinishLaunchingWithOptions:
  • Initial Frame Render:建立、执行布局,并绘制视图;提交渲染第一帧。loadView/viewDidLoad/layoutSubviews
  • Extended:该阶段为显示第一帧后的特定时间,该阶段会异步加载数据,你的 APP 在当前阶段应该是可交互、可响应的。

下面看下每一个阶段应该避免的作法。数据结构和算法

  • System Interface
    • 避免链接没用的 framework。
    • 避免在启动阶加载动态库。
    • 硬连接全部的依赖。
  • Runtime Init
    • 减小 +load 方法的调用。
    • 使用 +initialize 去懒加载静态初始化。
    • 暴露 framework 中的初始化 API。
  • UIKit Init
    • 减小 UIApplication 子类的工做。
    • 减小 UIApplicationDelegate 初始化的工做。
  • Application Init
    • 延后 UIApplicationDelegate 生命周期中不相关的工做。
    • 两个 scenes 之间共享资源。
  • Initial Frame Render
  • Extended
    • 应使用 os_signpost 去检测耗时代码。

介绍完启动类型和启动阶段,接下来看一下如何正确的测量。布局

正确的测量启动

在进行启动时间测量时,应对屡次测量环境保持干净一致的测量环境,这样测量的结果才会有说服力。post

能够经过下面的方法来保持环境的一致性:性能

  • 重启手机,并放置2-3min。
  • 启动飞行模式或者 mock 环境。
  • 不使用 iCloud 帐户,或者使用一致的 iCloud 帐户。
  • 使用 App 的 release 模式。
  • 测量热启动。

在测量过程当中咱们应对新旧设备都进行测试。

启动时间能够经过 XCTest 来进行测试。 截屏2021-05-10 下午4.53.33.png

如何优化启动时间

主要经过下面的三大块来优化。

减小工做量

  • 延后与第一帧不相关的工做。好比 application:willFinishLaunchingWithOptions: 里不重要的接口调用。
  • 移除阻塞主线层的代码。耗时的操做确定要放在子线程异步处理。
  • 减小内存的使用。

指定正确的优先级

这一块主要针对正确的使用 GCD。

  • 对于 task 使用正确的 Qos。
  • 利用 scheduler 来优化启动。
  • 使用正确的原语优先级。

优化代码

  • 简化已有代码。
  • 使用正确的数据结构和算法。
  • 缓存一切可缓存的。

总结

  • 优化启动时间很重要,咱们要让它成为平常开发的一部分。
  • 删除一切不须要的代码合资源。
  • 正确的使用 GCD、数据结构和算法。
  • 重构简化现有代码。
  • 尽量多的缓存。