close
当前位置: 物联网在线 > 技术文库 > ios >

今日头条 iOS 客户端启动速度优化

今日头条 iOS 客户端启动速度优化

应用启动时间,直接影响用户对一款应用的判断和使用体验。头条主app本身就包含非常多并且复杂度高的业务模块(如新闻、视频等),也接入了很多第三方的插件,这势必会拖慢应用的启动时间,本着精益求精的态度和对用户体验的追求,我们希望在业务扩张的同时最大程度的优化启动时间。

技术调研

先说结论,t(App总启动时间) = t1(main()之前的加载时间) + t2(main()之后的加载时间)。

t1 = 系统dylib(动态链接库)和自身App可执行文件的加载;

t2 = main方法执行之后到AppDelegate类中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法执行结束前这段时间,主要是构建第一个界面,并完成渲染展示。

main()调用之前的加载过程

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

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

其实无论对于系统的动态链接库还是对于App本身的可执行文件而言,他们都算是image(镜像),而每个App都是以image(镜像)为单位进行加载的,那么image究竟包括哪些呢?

什么是image

1.executable可执行文件 比如.o文件。

2.dylib 动态链接库 framework就是动态链接库和相应资源包含在一起的一个文件夹结构。

3.bundle 资源文件 只能用dlopen加载,不推荐使用这种方式加载。

除了我们App本身的可行性文件,系统中所有的framework比如UIKit、Foundation等都是以动态链接库的方式集成进App中的。

系统使用动态链接有几点好处:

代码共用:很多程序都动态链接了这些 lib,但它们在内存和磁盘中中只有一份。

易于维护:由于被依赖的 lib 是程序执行时才链接的,所以这些 lib 很容易做更新,比如libSystem.dylib 是 libSystem.B.dylib 的替身,哪天想升级直接换成libSystem.C.dylib 然后再替换替身就行了。

减少可执行文件体积:相比静态链接,动态链接在编译时不需要打进去,所以可执行文件的体积要小很多。

今日头条 iOS 客户端启动速度优化

如上图所示,不同进程之间共用系统dylib的 _TEXT 区,但是各自维护对应的 _DATA 区。

所有动态链接库和我们App中的静态库.a和所有类文件编译后的.o文件最终都是由dyld(the dynamic link editor),Apple的动态链接器来加载到内存中。每个image都是由一个叫做ImageLoader的类来负责加载(一一对应),那么ImageLoader又是什么呢?

什么是ImageLoader

image 表示一个二进制文件(可执行文件或 so 文件),里面是被编译过的符号、代码等,所以 ImageLoader 作用是将这些文件加载进内存,且每一个文件对应一个ImageLoader实例来负责加载。

两步走:

在程序运行时它先将动态链接的 image 递归加载 (也就是上面测试栈中一串的递归调用的时刻)。

再从可执行文件 image 递归加载所有符号。

当然所有这些都发生在我们真正的main函数执行前。

动态链接库加载的具体流程

动态链接库的加载步骤具体分为5步:

load dylibs image 读取库镜像文件

Rebase image

Bind image

Objc setup

initializers

load dylibs image

在每个动态库的加载过程中, dyld需要:

分析所依赖的动态库

找到动态库的mach-o文件

打开文件

验证文件

在系统核心注册文件签名

对动态库的每一个segment调用mmap()

通常的,一个App需要加载100到400个dylibs, 但是其中的系统库被优化,可以很快的加载。

针对这一步骤的优化有:

减少非系统库的依赖

合并非系统库

使用静态资源,比如把代码加入主程序

rebase/bind

由于ASLR(address space layout randomization)的存在,可执行文件和动态链接库在虚拟内存中的加载地址每次启动都不固定,所以需要这2步来修复镜像中的资源指针,来指向正确的地址。

rebase修复的是指向当前镜像内部的资源指针; 而bind指向的是镜像外部的资源指针。

rebase步骤先进行,需要把镜像读入内存,并以page为单位进行加密验证,保证不会被篡改,所以这一步的瓶颈在IO。bind在其后进行,由于要查询符号表,来指向跨镜像的资源,加上在rebase阶段,镜像已被读入和加密验证,所以这一步的瓶颈在于CPU计算。

通过命令行可以查看相关的资源指针:

xcrun dyldinfo -rebase -bind -lazy_bind myApp.App/myApp

优化该阶段的关键在于减少__DATA segment中的指针数量。我们可以优化的点有:

减少Objc类数量, 减少selector数量

减少C++虚函数数量

转而使用swift stuct(其实本质上就是为了减少符号的数量)

Objc setup

这一步主要工作是:

注册Objc类 (class registration)

把category的定义插入方法列表 (category registration)

保证每一个selector唯一 (selctor uniquing)

由于之前2步骤的优化,这一步实际上没有什么可做的。

initializers

以上三步属于静态调整(fix-up),都是在修改__DATA segment中的内容,而这里则开始动态调整,开始在堆和堆栈中写入内容。

在这里的工作有:

Objc的+load()函数

C++的构造函数属性函数 形如 attribute ((constructor)) void DoSomeInitializationWork()


(责任编辑:ioter)

用户喜欢...

对话斑马网络CEO施雪松:系统正式走向开放,撬动市场只需百万装机量 |钛媒体独家

一方是国企背景的汽车界龙头,一方是IT行业的体量之最,话语权和基因冲突曾让早期的斑马公司进展缓慢。斑马CE...


今日起正式取消手机长途和漫游费 但降费仍在路上

长途费、漫游费今日走向终点。9 月 1 日起,三大运营商将全面取消手机客户国内电话长途通话费和漫游通话费,对于...


应生产力变革之运而生:戴尔商用客户端新品亮相科技峰会

以“智行践远就绪数字未来”为主题的 2017戴尔科技峰会于2017年8月29、30日在上海世博中心成功举办。随着云计算、大...


360 N5s新版本今日发布 摄像头/处理器大变

同时该机还前置1300万像素摄像头,是首款采用前置PDAF相位对焦的手机,辅以200万像素景深摄像头,该机后置1300万相...


Velodyne阴影下,国产激光雷达如何在无人驾驶领域分杯羹

新入局者的机会在哪里?速腾聚创CEO邱纯鑫很有信心的说,多线激光雷达的技术本身都还没有成熟,有很大的发展空...


马云说智能世界三要素:互联网、大数据、云计算,机器是不可能取代人类的

6月29日上午,天津“世界智能大会”现场,马云作了万字演讲,36氪做了一定精简,以下是马云演讲整理: 我今天不...


新能源汽车新政冲击,积分新政或成双刃剑

在2020年实现200万辆新能源车产销量的目标之下,《双积分管理办法》的实施几乎势在必行。新能源汽车进入后补贴时...


物联网头条:物联网深度报告

互联网用户总数趋于饱和,获客成本快速增加。据CNNIC统计,在经历前几年的快速增长后,我国网民总数和移动互联网用户数量增速已趋于缓和。 1、物联网深度融入生活场景,爆发条件成熟...


奔驰推出了这款主打健康的概念车,说不定会让你“爱上”堵车

奔驰为什么把概念车发布会搬到了健身房,还大谈特谈瑜伽、茶道?...


物联网头条:物联网深度报告

互联网用户总数趋于饱和,获客成本快速增加。据CNNIC统计,在经历前几年的快速增长后,我国网民总数和移动互联网...