App 的启动速度不仅影响我们调试,也直接关系到用户体验。之前有些很久没有打开过的项目,需要花费很长的时间才完成编译;对应的 App 在点击后,许久才出现启动画面。你是否为这些问题苦恼过呢?

这是我观看 WWDC2016 Sessions406 《Optimizing App Start Time》的博客笔记。虽然没有字幕听起来很吃力,但光看 Slide 还是有不少收获的。原文有点长,包括理论是实践两部分,为了方便阅读这里只放出一部分。

Mach-O 文件是如何被加载的

在 mian() 函数执行之前,操作系统为我们做了什么?

exec() -> main()

exec()执行过程:
内核随机分配一段可用的内存给应用程序。但有个规则,即不分配低地址的内存空间,该地址空间大小由处理器的位数决定,情况如下:

  • 32位处理器保留 4K

  • 64位处理器保留 4G

低地址保留,用于保存空指针、异常错误信息等。

加载动态库

内存分配完成后,处理器运行动态加载器(Dynamic loader)来加载动态依赖库(dylibs)。

加载过程:

  1. 映射所有的直接依赖库,递归间接调用的依赖库(Map all dependent dylibs,recurse).

  2. 重建所有的图片(Rebase all images).

  3. 绑定所有图片(Bind all images).

  4. 准备代码层面的加载(ObjC prepare images)

  5. 执行初始化方法(Run initializes).

直接加载:

  • 解析所有的动态库

  • 找出需要的 mach-O 文件

  • 打开并读取文件

  • 认证 mach-O 文件

  • 登记代码签名

  • 为每个 segment 调用 mmap() 映射函数

递归加载:
当所有直接依赖库加载完毕后,还存在直接依赖库依赖于其他库的情况。递归加载间接依赖库,一般 App 需要加载 100 到 400 个动态库!其中绝大多数为系统库,苹果已经最优化了系统库的加载。

2. 重建(Rebasing)

将图片资源根据其地址进行加载,重建信息被编码在 LINKEDIT segment 中。重建的过程按照地址顺序执行,所以可以被内核预取。

3. 绑定(Binding)

应用程序对动态库的引用只是字符层(symbol)面上,绑定过程中需要加载器通过函数名来查找,相对于重建这个过程需要更多的计算。

4. ObjC 准备阶段
  • 完成重建和绑定后的配置工作

  • 登记定义的 ObjC 类

  • 更新实例变量对应的内存位置

  • 分类的方法被插入到主类

5. 初始化(Initializer)
  • 静态分配内存的对象的初始化

  • 调用 +load 方法

  • 调用相关联的动态库

  • 最后,执行 main()函数

启动优化

对于不同的设备启动速度都不相同,苹果认为完美的启动时间是在 400 毫秒以内。App 的最大允许启动时间为20秒,如果超过这个时间就会被 Killed。

我们可以通过编辑工程的 Schemes 来打印 App 的启动中各项时间花费,从而来分析和优化启动过程。
在 Arguments -> Environment Variables 中加入 DYLY_PRINT_STATISTICS

设置后,控制台部分项时间花费输出:

1. 加载动态库

加载动态库需要很大的开销,解决方法是尽可能减少引入不必要的动态库,合并现有可操作的动态库,使用静态文件,使用懒加载等

2. 重建和绑定过程
  • 减少指针变量的使用

  • 减少 Objective-C 类

  • 减少 C++ 虚基类的使用(少见)

  • 建议使用 Swift 结构体

  • 尽可能将属性设置为只读

3. 初始化
  • 将 +load 函数的操作尽可能在 +initialize 方法中执行

  • 简化 C++ 构造函数

  • 不要在初始化方法里面调用 dlopen()

  • 不要在初始化方法里面创建线程

+initialized 会在任何类加载之前被调用,而 +load 是所在类被加载到系统的时候被调用,这通常比 +initialized 调用的时机要早。在 +load 函数里面做操作会延缓系统的启动时间。

dlopen() 函数用于打开 Bundle 尽可能的延迟读取本地的 Bundld 资源,也有利于加快启动速度。

线程的创建需要很大的开销,不要在 App 启动的过程创建子线程。

参考阅读

1. https://developer.apple.com/videos/play/wwdc2016/406/
2. http://objccn.io/issue-6-3/
3. https://code.facebook.com/posts/1675399786008080/optimizing-facebook-for-ios-start-time/

优化 App 的启动速度的更多相关文章

  1. Android性能优化-App启动优化

    原文地址:https://developer.android.com/topic/performance/launch-time.html#common 通常用户期望app响应和加载速度越快越好.一个 ...

  2. 优化 App 的启动时间

    这是一篇 WWDC 2016 Session 406 的学习笔记,从原理到实践讲述了如何优化 App 的启动时间. App 运行理论 main() 执行前发生的事 Mach-O 格式 虚拟内存基础 M ...

  3. Android性能优化-App后台优化

    原文链接 Background Optimizations 前言 后台进程是内存和电池敏感的.一个隐式的broadcast可能会启动很多监听它的后台进程,即使这些进程可能做得工作不多.这可能丢设备性能 ...

  4. 怎样优化app,看Facebook怎样做

    周四,Facebook Engineering blog 发表了一篇名为<Improving Facebook on Android>博文.博文从四个方面(Performance,Data ...

  5. WWDC2018 之 优化 App Assets Optimizing App Assets

    该篇博客记录了观看WWDC Session227<Optimizing App Assets>的内容以及一些理解. 引言 该session主要讲述了使用Assets Catalog的新特性 ...

  6. 如何优化 App 的启动时间

    http://www.cocoachina.com/ios/20161102/17931.html App 运行理论 main() 执行前发生的事 Mach-O 格式 虚拟内存基础 Mach-O 二进 ...

  7. iOS 如何优化 App 的启动时间

    App 运行理论 main() 执行前发生的事 Mach-O 格式 虚拟内存基础 Mach-O 二进制的加载 理论速成 Mach-O 术语 Mach-O 是针对不同运行时可执行文件的文件类型. 文件类 ...

  8. [Android Memory] Android Zipalign zip对齐优化app程序

    转载地址:http://www.cnblogs.com/xirihanlin/archive/2010/04/12/1710164.html 参考文章:http://www.cnblogs.com/l ...

  9. 5个可以帮你优化App的优秀网站

    也许现在有一款App可以提供所有你需要的,你不需要的,或者你可以想象到的内容.但是,有多少App真的可以不仅满足需求而且还能提供很好的用户体验呢? 相信很多APP并没有这样的能力.有一些APP的设计特 ...

随机推荐

  1. 使用sem_post信号量进行线程同步

    写了一小段程序,测试一下线程同步的问题,如下: #include <stdio.h> #include <string.h> #include <semaphore.h& ...

  2. Angular 中得 scope 作用域梳理

    $scope 的使用贯穿整个 Angular App 应用,它与数据模型相关联,同时也是表达式执行的上下文.有了 $scope 就在视图和控制器之间建立了一个通道,基于作用域视图在修改数据时会立刻更新 ...

  3. Android 保存联系人,包括部门\职位\传真\地址\照片

    private void toSaveContactInfo() { ContentValues values = new ContentValues(); // 首先向RawContacts.CON ...

  4. SQL2005/8数据库提示单个用户无法操作的解决方法

    原因分析: 是操作数据库的用户被锁定了,思路是通过查找目标用户,将其解锁即可,可是这样太麻烦了. 解决办法执行如下sql: USE master;  GO  DECLARE @SQL VARCHAR( ...

  5. 网页布局:float与position的区别

    网页开发中布局是一个永恒的话题.巧妙的布局会让网页具有良好的适应性和扩展性.css的布局主要涉及两个属性——position和float.它们俩看上去很容易被弄混,可是仔细分析一下,它们的区别还是很明 ...

  6. <!DOCTYPE> 标签的深度剖析以及使用选择

    前言: 今天被问道“有没有仔细了解过<!DOCTYPE>标签?”,愣了一下,因为一开始在W3cschool上看到过建议使用XHTML Transitional DTD,之后就很听话地把Dr ...

  7. Spring中的设计模式

    [Spring中的设计模式] http://www.uml.org.cn/j2ee/201301074.asp [详解设计模式在Spring中的应用]    [http://www.geek521.c ...

  8. jqGrid初次使用遇到的问题及解决方法

    问题一:初始化定义翻页用的导航栏时,表中出现"undefined"方框: 解决:需要导入grid.locale-cn.js文件. 问题二:页面只有一页,无法翻页: 解决:初始化设置 ...

  9. CSS超出2行省略号

    overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; line- ...

  10. xx.exe 中的 0x7c92e4df 处最可能的异常: 0xC0000008: An invalid handle was specified

    今天遇到个超级奇怪的问题,昨天还好端端的程序,今天用VS打开后,在关闭主窗口的时候居然弹出错误提示:xx.exe 中的 0x7c92e4df 处最可能的异常: "0xC0000008: An ...