应用启动
应用启动时间长短对用户第一次体验至关重要,应用的快速启动,能够给你的用户一个好的体验。
应用最理想的启动时间在400毫秒,但永远不能超过20秒,如果超过20秒,操作系统(watchdog)会杀了你的应用程序,然后产生0x8badf00d的crash report。值得注意的是,Xcode在Debug的时候,会禁止“watchdog”。
应用包括冷启动和热启动两种:
冷启动。指的是当应用还没准备好运行时,我们必须加载和构建整个应用。这包括设置屏幕底部的分栏菜单,确保用户是否被合适地登录,以及处理其他更多的事情。“引导”程序是在applicationDidFinishLaunching:withOptions:方法中开始的。
热启动。指的是应用已经运行但是在后台被挂起(比如用户点击了 home 健),我们只需要知道在何时应用进入后台。在这种情况下,我们的应用通过 applicationWillEnterForeground: 接收到前台的事件,紧接着应用恢复。
可见冷启动测量更为重要。
时间测量
应用启动过程:parse images, map images, rebase images, bind images, run image initializers, call main(), call UIApplicationMain()、Call applicationWillFinishLaunching. 但这里只讲 call main()之前的时间测量、优化。
下面使用DYLD环境变量 DYLD_PRINT_STATISTICS 测量 main()之前的时间。

点击’Eidt schemes’,添加DYLD_PRINT_STATISTICS环境变量。然后重新运行,可以看到console的输出:
|
|
附表: 其他DYLD_PRINT环境变量
| Environment variable | Description |
|---|---|
| DYLD_PRINT_LIBRARIES | Logs when images are loaded. |
| DYLD_PRINT_LIBRARIES_POST_LAUNCH | Logs when images are loaded as a result of a dlopen call. Includes a dynamic libraries’ dependent libraries. |
| DYLD_PRINT_STATISTICS | Logs statistical information on an application’s launch process, such as how many images were loaded, when the application finishes launching. |
| DYLD_PRINT_INITIALIZERS | Logs when the dynamic loader calls initializer and finalizer functions. |
| DYLD_PRINT_SEGMENTS | Logs when the dynamic loader maps a segment of a dynamic library to the current process’s address space. |
| DYLD_PRINT_BINDINGS | Logs when the dynamic loader binds an undefined external symbol with its definition. |
Dylib 加载
平均应用有100到400个dylibs。其中大部分是系统dylibs,且系统dylibs加载是非常快速的。另外从Xcode6开始支持开发者创建使用自己的动态库了。
在这里,我们只需使用更少的dylibs,合并现有dylibs,使用静态库;或者使用dlopen()延迟加载,但它会造成性能问题,它实际上导致了后来做更多的工作,但它推迟加载了。
Rebase/Binding
减少 __DATA pointers
减少 Objective C 元数据(Classes, selectors, and categories)
减少使用 C++ 虚拟函数
使用 Swift的structs
对于 machine generated code,使用 offsets 代替 pointers
ObjC Setup
这一过程完成以下事情,而且都是必须的:
Class registration
Non-fragile ivars offsets updated
Category registration
Selector uniquing
Initializers
ObjC 使用 +initiailize() 代替 +load()
C/C++ 使用 attribute((constructor)),它会在 main()函数执行之前被自动的执行。
使用dispatch_once()、pthread_once()、std::once()懒初始化
Do not call dlopen() in initializers
Do not create threads in initializers
我们通过减少dylibs的数量,降低ObjC类的数量,并消除static initializers,来减少应用程序启动时间,还可以使用Swift改善它。更多内容在Optimizing App Startup Time