iOS 启动时间优化
在 WWDC 2016 上首次提到了关于 App 应用启动速度优化的话题:Session 406 Optimizing App Startup Time。
一、冷启动与热启动
热启动是,APP会恢复之前的状态继续运行,这种就是热启动,我们平时所说的APP在后台的存活时间,其实就是APP能执行热启动的最大时间间隔。而冷启动则是APP从被加载到内存到运行的状态,下面我们要讲的主要是冷启动。
- 热启动:由于某种原因,APP 的状态由 running 切换为 suspend,但此时 APP 并没有被系统 kill掉,当我们再次把 APP 切换到前台的时候,此时启动 app 所需要的数据仍然在缓存中,再次启动的时候称为热启动。通常情况下热启动能帮助提升启动速度,但有时也可能会出现 app 卡死手动退出进程后重新打开仍然是卡死状态。
- 冷启动:如果是比较长时间没有启动过 app 或者设备刚刚重启,这种情况下启动 app,就被称为冷启动。
二、查看启动时间
- 最佳速度:400ms。因为不添加任何同步任务从图标被点击到显示 Launch Screen,然后 Launch Screen 消失这段时间就是 400ms。如果 app 启动时间接近这个数值,那证明 app 的启动任务已经优化到最佳。
- 最慢速度:不可以大于 20s,否则会被系统杀掉。
配置 Xcode 环境变量在日志中打印启动时间:
- 打开工程 -> Edit Scheme -> Run -> Environment Variables
根据需要添加 DYLD_PRINT_STATISTICS
和 DYLD_PRINT_STATISTICS_DETAILS
环境变量。1 表示 YES,开启这个功能。
Total pre-main time: 617.58 milliseconds (100.0%)
dylib loading time: 472.75 milliseconds (76.5%)
rebase/binding time: 27.01 milliseconds (4.3%)
ObjC setup time: 28.90 milliseconds (4.6%)
initializer time: 88.76 milliseconds (14.3%)
slowest intializers :
libSystem.B.dylib : 8.81 milliseconds (1.4%)
libMainThreadChecker.dylib : 14.42 milliseconds (2.3%)
AFNetworking : 18.43 milliseconds (2.9%)
Realm : 20.98 milliseconds (3.3%)
CYKJBasic : 12.96 milliseconds (2.0%)
包括执行以下步骤的所有时间
- 解析镜像
- 映射镜像
- Rebase 镜像
- Bind 镜像
- 镜像初始化
- 调用 main()方法
- 调用 UIApplicationMain() 方法
- 调用 applicationWillFinishLaunching 回调
DYLD_PRINT_STATISTICS 环境变量只能帮助测量 Dyld 的启动时间,无法帮助我们完整地测量 main() 方法执行之前的所有时间。
为了能够解析 dylib 的符号表,debugger 需要在加载每个 dylib 的时候暂停一下,同时通过 USB 线来传输这些数据,这是非常耗时的,但是 DYLD_PRINT_STATISTICS 在测量的时候已经减去了这些耗时,因此不用担心在 debug 模式下数据的准确性,而且测量出的耗时通常会比肉眼计算出来的耗时要小。
三、优化启动
启动时间其实包括了两部分:main 函数之前和 main 函数到第一个界面的 viewDidAppear:。
所以,优化也是从两个方面进行的,优化效果主要来自于后者,因为绝大多数 App 的瓶颈在自己的代码里。而对于 pre-main 的优化能做的无非是减少不必要的动态库引用、多个库合并成一个,从上面的打印数据也可以看出,主要耗时是在 dylib loading
,消耗 78.8%
的时间。
四、Main 函数之后
从 main 函数开始执行,到第一个界面显示,期间一般做以下任务:
- 执行 AppDelegate 的代理方法,主要是 didFinishLaunchingWithOptions
- 初始化 Window,初始化基础的 ViewController 结构
- 获取数据(Local DB/Network),展示给用户。
优化:
使用代码绘制 UI,减少或者不用 xib 和 storyboard
延迟初始化和加载不必要的 UIViewController 和 View。
比如 UITabBarController 有四个 Item,在启动的时候尽量只初始化首页的页面,其它 Item 页面先用空 VC 占位。而且首页的内容中不必要的内容也可以先不初始化,做成懒加载形式,在用户确实需要查看和使用时再初始化。
对于确实需要启动时使用但又比较耗时的事物放倒后台处理,如果涉及到 UI 则在处理完成后把刷新任务放回主线程。
- 日志功能,日志往往涉及到 DB 操作;
- 文件读取,比如读取本地存储的省份城市区县文件和图片处理;
- 大量的计算,比如图片处理,比较大的 json 数据转 Model;
能延迟初始化的尽量延迟初始化
三方 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%)
Session 所要传达的内容:
- 使用 DYLD_PRINT_STATISTICS 测试启动加载时间
- 减少自定义的动态库集成
- 精简原有的 Objective-C 类和代码
- 移除静态的初始化操作
- 使用更多的 Swift 代码
优化:
loading dylib
启动的第一步是加载动态库,加载系统的动态库使很快的,因为可以缓存,而加载内嵌的动态库速度较慢。所以,提高这一步的效率的关键是:减少动态库的数量。
加载 App 内嵌的动态库比较耗时,因为每加载一个动态库,系统都需要文件验证、注册签名、针对 segment 进行 mmap。embedded framework 一般用于 Extension 跟主 APP 共享代码逻辑。
合并动态库,比如公司内部由私有 Pod 建立了如下动态库:XXTableView、XXHUD、XXLabel,强烈建议合并成一个 XXUIKit 来提高加载速度。尽量保证将 App 现有的非系统级的动态库个数保证在 6 个以内.
也可以使用静态库,虽然会增加些许 rebase/binding 的时间。
rebase/binding
Rebase 和 Binding 都是为了解决指针引用的问题。所以为了加快 rebase/binding,则需要更少的做指针修复。当你的 app 当中有太多的 Objective-C 的类、方法选择器和类别,会增加这一部分的启动时间。有一个数据:当大于 20000 个时候,会增加 800ms 的时间。
对于 Objective-C 开发来说,主要的时间消耗在 Class/Method 的符号加载上,所以常见的优化方案是:
- 减少 __DATA 段中的指针数量。
- 合并 Category 和功能类似的类,减少唯一 Selector 的个数。主要是为了加快程序的整个动态链接,在进行动态库的重定位和绑定(Rebase/binding)过程中减少指针修正的使用,加快程序机器码的生成。
- 删除无用的方法和类。
- 多用 Swift Structs,因为 Swfit Structs 是静态分发的。可以参考Swift进阶之内存模型和方法调度
- 减少 c++ 虚函数
ObjC setup time
在 Objective-C 的运行时(runtime),需要对类(class),类别(category)进行注册,以及选择器的分配,所以参照 rebase/binding time,尽量减少类的数量,可以达到减少这一部分的时间。
Initializers
- 用 initialize 替代 load。load 在程序启动的时候就会调用,而且必须阻塞等着所有类的 load 方法都执行完;initialize 在类首次使用的时候调用。
- 减少使用 c/c++ 的 __atribute__((constructor))。推荐使用 dispatch_once()、pthread_once()、std:once()等方法;
- 推荐使用 swift
- 不要在初始化中调用 dlopen() 方法,因为加载过程是单线程,无锁,如果调用 dlopen 则会变成多线程,会开启锁的消耗,同时有可能死锁
- 不要在初始化中创建线程
文章
iOS 启动时间优化的更多相关文章
- iOS性能优化:Instruments使用实战
iOS性能优化:Instruments使用实战 最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下. Instrument ...
- IOS 性能优化的建议和技巧
IOS 性能优化的建议和技巧 本文来自iOS Tutorial Team 的 Marcelo Fabri,他是Movile的一名 iOS 程序员.这是他的个人网站:http://www.marcelo ...
- iOS启动速度优化
背景 7月26号我们阿里数据iOS端发布了4.4.0版本,这次版本主要是优化了性能,其中main()阶段的启动耗时优化成果比较明显,从之前的0.5-0.7秒,降低为目前的0.1-0.2秒(main() ...
- iOS性能优化总结
iOS性能优化总结.关于 iOS 性能优化梳理: 基本工具.业务优化.内存优化.卡顿优化.布局优化.电量优化. 安装包瘦身.启动优化.网络优化等. 关于iOS 性能优化梳理: 基本工具.业务优化.内存 ...
- iOS 项目优化
前言 iOS性能优化系列篇之"优化总体原则" 不要提前过度优化 要找到性能瓶颈 要在不同性能指标间权衡 要理解优化任务的底层运行机制 要有技术保障体系 一.启动速度优化 1.1 学 ...
- 【腾讯Bugly干货分享】微信读书iOS性能优化
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578c93ca9644bd524bfcabe8 “8小时内拼工作,8小时外拼成长 ...
- iOS 滑动性能优化
iOS 滑动性能优化 目录 一. 减少图层的Blend操作 1. UIView的背景色避免使用clearColor 2. 控件贴图避免使用带alpha的图片 3. UIImageView 使用时避免半 ...
- Linux下启动时间优化专题
1. 过往优化总结 陆陆续续在Linux进行启动时间优化,之前有两份文档,分别从内核和用户空间两个方向进行了优化. <Android/Linux boot time分析优化>和<Bu ...
- [Startup].Linux启动时间优化
转自:https://www.cnblogs.com/agui125/p/10070559.html 正文 回到顶部 1. 实践过程 我是对海思3559进行启动时间优化的.具体的操作可以参考<H ...
随机推荐
- 密码学习(一)——Base64
简介 Base64是一种非常常用的数据编码方式,标准Base64可以把所有的数据用"A~Z,a~z,0~9,+,/,="共65个字符(‘=’号仅是一个占位符,作为后缀)表示,当然在 ...
- Yuchuan_linux_C 编程之八 文件操作相关函数
一.整体大纲 st_mode整体介绍: st_mode详细介绍: 二. Linux文件操作相关函数 1. stat 作用:获得文件信息,也可以获取文件大小. 头文件 #include <sys/ ...
- frida报错frida.InvalidArgumentError: device not found问题解决方案
一.问题描述 python安装好frida框架后,在安卓端启动了frida-server,启动要hook的应用,在cmd中执行python脚本,报错frida.InvalidArgumentE ...
- NLP自然语言处理入门-- 文本预处理Pre-processing
引言 自然语言处理NLP(nature language processing),顾名思义,就是使用计算机对语言文字进行处理的相关技术以及应用.在对文本做数据分析时,我们一大半的时间都会花在文本预处理 ...
- selenium+Python 将登录模块化
公共模块化:(登录) login.py from selenium import webdriver from time import sleep class login(): def u ...
- ubuntu16.04 + caffe + SSD 硬件配置
搞了几个月,终于把SSD试通了,不是科班出身的就是弯路多啊.几个月才跑通了caffe + ssd,痛苦至极,好在柳暗花明.好了,废话不多说,入正题. SSD作为object detection的论文, ...
- 原生js实现随着滚动条滚动,导航会自动切换的效果
最近学习前端,把前面用原生js写的一段有关tab切换效果的代码贴上,实现的效果比较简陋,请大家见谅 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1 ...
- Python 小技巧:如何实现操作系统兼容性打包?
有一个这样的问题:现要用 setuptools 把一个项目打包成 whl 文件,然后 pip install 在 Windows/Linux 两种操作系统上,但是该项目中有一些依赖库只有 Window ...
- 【JAVA进阶架构师指南】之一:如何进行架构设计
前言 本博客是长篇系列博客,旨在帮助想提升自己,突破技术瓶颈,但又苦于不知道如何进行系统学习从而提升自己的童鞋.笔者假设读者具有3-5年开发经验,java基础扎实,想突破自己的技术瓶颈,成为一位优 ...
- [Java网络安全系列面试题]常见web攻击有哪些?
搞Web开发离不开安全这个话题,确保网站或者网页应用的安全性,是每个开发人员都应该了解的事.本篇主要简单介绍在Web领域几种常见的攻击手段及Java Web中的预防方式. 1. XSS 1.1 什么是 ...