1. App启动过程

  • 解析Info.plist

    • 加载相关信息,例如如闪屏

    • 沙箱建立、权限检查

  • Mach-O加载

    • 如果是胖二进制文件,寻找合适当前CPU类别的部分

    • 加载所有依赖的Mach-O文件(递归调用Mach-O加载的方法)

    • 定位内部、外部指针引用,例如字符串、函数等

    • 执行声明为__attribute__((constructor))的C函数

    • 加载类扩展(Category)中的方法

    • C++静态对象加载、调用ObjC的 +load 函数

  • 程序执行

    • 调用main()

    • 调用UIApplicationMain()

    • 调用applicationWillFinishLaunching

2. 如何测量启动过程耗时

冷启动比热启动重要

当用户按下home键的时候,iOS的App并不会马上被kill掉,还会继续存活若干时间。理想情况下,用户点击App的图标再次回来的时候,App几乎不需要做什么,就可以还原到退出前的状态,继续为用户服务。这种持续存活的情况下启动App,我们称为热启动,相对而言冷启动就是App被kill掉以后一切从头开始启动的过程。我们这里只讨论App冷启动的情况。

main()函数之前

在不越狱的情况下,以往很难精确的测量在main()函数之前的启动耗时,因而我们也往往容易忽略掉这部分数据。小型App确实不需要太过关注这部分。但如果是大型App(自定义的动态库超过50个、或编译结果二进制文件超过30MB),这部分耗时将会变得突出。所幸,苹果已经在Xcode中加入这部分的支持。

苹果提供的方法
  • 在Xcode的菜单中选择ProjectSchemeEdit Scheme...,然后找到 Run → Environment Variables →+,添加name为DYLD_PRINT_STATISTICSvalue1的环境变量。

  • 在Xcode运行App时,会在console中得到一个报告。例如,我在WiFi管家中加入以上设置之后,会得到这样一个报告:

    Total pre-main time:  94.33 milliseconds (100.0%)
            dylib loading time:  61.87 milliseconds (65.5%)
           rebase/binding time:   3.09 milliseconds (3.2%)
               ObjC setup time:  10.78 milliseconds (11.4%)
              initializer time:  18.50 milliseconds (19.6%)
              slowest intializers :
                libSystem.B.dylib :   3.59 milliseconds (3.8%)
      libBacktraceRecording.dylib :   3.65 milliseconds (3.8%)
                       GTFreeWifi :   7.09 milliseconds (7.5%)
如何解读
  1. main()函数之前总共使用了94.33ms

  2. 在94.33ms中,加载动态库用了61.87ms,指针重定位使用了3.09ms,ObjC类初始化使用了10.78ms,各种初始化使用了18.50ms。

  3. 在初始化耗费的18.50ms中,用时最多的三个初始化是libSystem.B.dylib、libBacktraceRecording.dylib以及GTFreeWifi。

main()函数之后

main()函数开始至applicationWillFinishLaunching结束,我们统一称为main()函数之后的部分。

3. 影响启动性能的因素

App启动过程中每一个步骤都会影响启动性能,但是有些部分所消耗的时间少之又少,另外有些部分根本无法避免,考虑到投入产出比,我们只列出我们可以优化的部分:

main()函数之前耗时的影响因素
  • 动态库加载越多,启动越慢。

  • ObjC类越多,启动越慢

  • C的constructor函数越多,启动越慢

  • C++静态对象越多,启动越慢

  • ObjC的+load越多,启动越慢

实验证明,在ObjC类的数目一样多的情况下,需要加载的动态库越多,App启动就越慢。同样的,在动态库一样多的情况下,ObjC的类越多,App的启动也越慢。需要加载的动态库从1个上升到10个的时候,用户几乎感知不到任何分别,但从10个上升到100个的时候就会变得十分明显。同理,100个类和1000个类,可能也很难查察觉得出,但1000个类和10000个类的分别就开始明显起来。

同样的,尽量不要写__attribute__((constructor))的C函数,也尽量不要用到C++的静态对象;至于ObjC的+load方法,似乎大家已经习惯不用它了。任何情况下,能用dispatch_once()来完成的,就尽量不要用到以上的方法。

main()函数之后耗时的影响因素
  • 执行main()函数的耗时

  • 执行applicationWillFinishLaunching的耗时

  • rootViewController及其childViewController的加载、view及其subviews的加载

优化的目标

由于每个App的情况有所不同,需要加载的数据量也有所不同,事实上我们无法使用一种统一的标准来衡量不同的App。苹果。

  • 应该在400ms内完成main()函数之前的加载

  • 整体过程耗时不能超过20秒,否则系统会kill掉进程,App启动失败

400ms内完成main()函数前的加载的建议值是怎样定出来的呢?其实我也没有太深究过这个问题,但是,当用户点击了一个App的图标时,iOS做动画到闪屏图出现的时长正好是这个数字,我想也许跟这个有关。

针对不同规模的App,我们的目标应该有所取舍。例如,对于像手机QQ这种集整个SNG的代码大成撸出来的App,对动态库的使用在所难免,但对于WiFi管家,由于在用户连接WiFi的时候需要非常快速的响应,所以快速启动就非常重要。

那么,如何定制优化的目标呢?首先,要确定启动性能的界限,例如,在各种App性能的指标中,哪一此属于启动性能的范畴,哪一些则于App的流畅度性能?我认为应该首先把启动过程分为四个部分:

  1. main()函数之前

  2. main()函数之后至applicationWillFinishLaunching完成

  3. App完成所有本地数据的加载并将相应的信息展示给用户

  4. App完成所有联网数据的加载并将相应的信息展示给用户

1+2一起决定了我们需要用户等待多久才能出现一个主视图,同时也是技术上可以精确测量的时长,1+2+3决定了用户视觉上的等待出现有用信息所需要的时长,1+2+3+4决定了我们需要多少时间才能让我们需要展示给用户的所有信息全部出现。

淘宝的iOS客户端无疑是各部分都做得非常优秀的典型。它所承载的业务完全不比微信和手机QQ少,但几乎瞬间完成了启动,并利用缓存机制使得用户马上看到“貌似完整”的界面,然后立即又刷新了刚刚联网更新回来的信息。也就是说,无论是技术上还是视觉上,它都非常的“快”。

启动优化实践

1. 移除不需要用到的动态库

2. 移除不需要用到的类

3. 合并功能类似的类和扩展(Category)

4. 压缩资源图片

5. 优化applicationWillFinishLaunching

6. 优化rootViewController加载

iOS App 启动性能优化的更多相关文章

  1. 马蜂窝 iOS App 启动治理:回归用户体验

    增长.活跃.留存是移动 App 的常见核心指标,直接反映一款 App 甚至一个互联网公司运行的健康程度和发展动能.启动流程的体验决定了用户的第一印象,在一定程度上影响了用户活跃度和留存率.因此,确保启 ...

  2. 李洪强iOS开发之性能优化技巧

    李洪强iOS开发之性能优化技巧 通过静态 Analyze 工具,以及运行时 Profile 工具分析性能瓶颈,并进行性能优化.结合本人在开发中遇到的问题,可以从以下几个方面进行性能优化. 一.view ...

  3. iOS 25个性能优化/内存优化常用方法

    1. 用ARC管理内存 ARC(Automatic ReferenceCounting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露.它自动为你 ...

  4. [iOS Animation]-CALayer 性能优化

    性能优化 代码应该运行的尽量快,而不是更快 - 理查德 在第一和第二部分,我们了解了Core Animation提供的关于绘制和动画的一些特性.Core Animation功能和性能都非常强大,但如果 ...

  5. iOS面试-关于性能优化

    目录 我要给出的建议将分为三个不同的等级: 入门级. 中级和进阶级: 入门级(这是些你一定会经常用在你app开发中的建议) 1. 用ARC管理内存2. 在正确的地方使用reuseIdentifier3 ...

  6. IOS (APP 启动 相应处理)

    APP 每次启动的入口都是通过: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSD ...

  7. 复杂TableView在iOS上的性能优化

    声明:本文翻译自<iOS performance optimization>,原文作者 Khang Vo.翻译本文纯属为了技术交流的目的,并不具有任何的商业性质,也不得利用本文内容进行商业 ...

  8. Windows 程序启动性能优化(先载入EXE,后载入DLL,只取有限的代码载入内存,将CPU的IP指向程序的入口点)

    一.重定位链接时重定位:目标文件一般由多个节组成,编译器在编译每个目标文件时一般都是从0地址开始生成代码.当多个代码节合成一个代码段时,需要根据其在最终代码段中的位置做出调整.同时,链接器需要对已经解 ...

  9. appium + java + WebDriverAgent实现IOS app启动

    Appium v1.8.1 <dependency>    <groupId>io.appium</groupId>    <artifactId>ja ...

随机推荐

  1. 个性化WinPE封装方法----制作过程需要了解的“命令”

    1.在现有的Windows7条件下,自动在E盘建立mywinpe文件夹,设置 Windows PE 构建环境,并保存到E:\mywinpe下 copype.cmd x86 E:\mywinpe 2.将 ...

  2. 图像处理------颜色梯度变化 (Color Gradient)

    有过UI设计经验的一定对2D图形渲染中的Color Gradient 或多或少有些接触,很多编程 语言也提供了Gradient的接口,但是想知道它是怎么实现的嘛? 本文介绍三种简单的颜色梯度变化算法, ...

  3. zTree实现地市县三级级联DAO接口

    zTree实现地市县三级级联DAO接口 ProvinceDao.java: /** * @Title:ProvinceDao.java * @Package:com.gwtjs.dao * @Desc ...

  4. MyEclipse提示

    MyEclipse提示 1.具体如下图 2.提示原因 3.解决办法

  5. C#数据缓存介绍及Caching通用帮助类整理

    C#缓存主要是为了提高数据的读取速度.因为服务器和应用客户端之间存在着流量的瓶颈,所以读取大容量数据时,使用缓存来直接为客户端服务,可以减少客户端与服务器端的数据交互,从而大大提高程序的性能. 以下为 ...

  6. Eclipse远程debug服务器

    一,找端口号 二,Eclipse配置 三,测试是否成功 四,结束远程debug

  7. CentOS7.2编译安装PHP7.2.3之史上最详细步骤。

    首先,我们的CentOS版本信息如下: 开始我们的编译. 第一步: 将php安装包安装到/usr/src目录下. cd /usr/src && wget http://cn2.php. ...

  8. 搭建web服务器-tomcat+apache+mysql+eclipse

    1. 下载并安装jdk 注意环境变量的配置: java_home: jdk的路径  path:.;%java_home%\bin;%java_home%\jre\bin classpath :  .; ...

  9. IT企业如何实现项目管理信息化的目标

    随着信息化技术的不断深入,企业管理方式逐渐向信息化管理转变.大部分IT企业也为了适应企业管理方式的变革,开始加强对管理信息化创新方面的建设.而IT企业在实现信息化的进程中,项目管理信息化其实是IT企业 ...

  10. 关于使用Ajax请求json数据,@RequestMapping返回中文乱码的几种解决办法

    一.问题描述: 使用ajax请求json数据的时候,无论如何返回的响应编码都是ISO-8859-1类型,因为统一都是utf-8编码,导致出现返回结果中文乱码情况. $.ajax({ type:&quo ...