iOS-APP启动完成过程,以及耗时优化
Posted MinggeQingchun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS-APP启动完成过程,以及耗时优化相关的知识,希望对你有一定的参考价值。
一、APP启动完成过程
1、解析Info.plist
加载相关信息,例如如闪屏
沙箱建立、权限检查
2、Mach-O加载
(1)Mach-O格式全称为Mach Object文件格式的缩写
(2)Mach-O文件类型分类:
1.Executable:应用可执行的二进制文件,如.m/.h文件经过编译后会生成对应的Mach-O文件
2.Dylib Library:动态链接库
3.Static Library:静态链接库
4.Bundle:不能被链接 Dylib,只能在运行使用dlopen()加载
5.Relocatable Object File:可重定向文件类型
(3)Mach-O文件结构
参考苹果官方文档,Mach-O文件结构由Header,Load Commands,Data三部分组成
如果是胖二进制文件,寻找合适当前CPU类别的部分
加载所有依赖的Mach-O文件(递归调用Mach-O加载的方法)
定位内部、外部指针引用,例如字符串、函数等
执行声明为__attribute__((constructor))的C函数
加载类扩展(Category)中的方法
C++静态对象加载、调用ObjC的 +load 函数
3、程序执行
(1)main函数
(2)执行UIApplicationMain函数
1、创建UIApplication对象
2、创建UIApplicationDelegate对象并复制
3、读取配置文件info.plist,设置程序启动的一些属性,(关于info.plist的内容可网上搜索下)
4、创建应用程序的Main Runloop循环
(3)UIApplicationDelegate对象开始处理监听到的事件
1、程序启动成功之后,首先调用application:didFinishLaunchingWithOptions:方法,
如果info.plist文件中配置了启动storyboard文件名,则加载storyboard文件。
如果没有配置,则根据代码来创建UIWindow--->UIWindow的rootViewController-->显示
二、影响性能因素
1、main()函数之前耗时的影响因素
动态库加载越多,启动越慢。
ObjC类越多,启动越慢
C的constructor函数越多,启动越慢
C++静态对象越多,启动越慢
ObjC的+load越多,启动越慢
2、main()函数之后耗时的影响因素
执行main()函数的耗时
执行applicationWillFinishLaunching的耗时
rootViewController及其childViewController的加载、view及其subviews的加载
三、耗时优化
进入 main() -> UIApplicationMain -> 初始化回调 -> 显示UI。
ios 的 App 启动时长大概可以这样计算:
t(App 总启动时间) = t1(main 调用之前的加载时间) + t2(main 调用之后的加载时间)。
t1 = 系统 dylib(动态链接库)和自身 App 可执行文件的加载。
t2 = main 方法执行之后到 AppDelegate 类中的 application:didFinishLaunchingWithOptions:方法执行结束前这段时间,主要是构建第一个界面,并完成渲染展示。
1、在 t1 阶段加快 App 启动的建议:
尽量使用静态库(.a和.framework(开发者自己创建的)),减少动态库(动态库形式:.dylib和.framework(系统的))的使用,动态链接比较耗时。
.a是纯二进制文件, .framework除了二进制文件还包含头文件和资源文件
.a不能直接使用,需要头文件(.h)配合;framework可直接使用
.a + .h + source = .framework
如果要用动态库,尽量将多个 dylib 动态库合并成一个。
尽量避免对系统库使用 optional linking,如果 App 用到的系统库在你所有支持的系统版本上都有,就设置为 required,因为 optional 会有些额外的检查。
减少 Objective-C Class、Selector、Category 的数量。可以合并或者删减一些 OC 类。
删减一些无用的静态变量,删减没有被调用到或者已经废弃的方法。
将不必须在 +load 中做的事情尽量挪到+initialize中,+initialize 是在第一次初始化这个类之前被调用,+load 在加载类的时候就被调用。尽量将+load里的代码延后调用。
尽量不要用 C++ 虚函数,创建虚函数表有开销。
不要使用 __atribute__((constructor)) 将方法显式标记为初始化器,而是让初始化方法调用时才执行。比如使用 dispatch_once(),pthread_once() 或 std::once()。
在初始化方法中不调用 dlopen(),dlopen() 有性能和死锁的可能性。
在初始化方法中不创建线程。
2、在 t2 阶段加快 App 启动的建议:
尽量不要使用 xib/storyboard,而是用纯代码作为首页 UI。
如果要用 xib/storyboard,不要在 xib/storyboard 中存放太多的视图。
对application:didFinishLaunchingWithOptions:里的任务尽量延迟加载或懒加载。
不要在 NSUserDefaults 中存放太多的数据,NSUserDefaults 是一个 plist 文件,plist 文件被反序列化一次。
避免在启动时打印过多的 log。
少用 NSLog,因为每一次 NSLog 的调用都会创建一个新的 NSCalendar 实例。
每一段 SQLite 语句都是一个段被编译的程序,调用 sqlite3_prepare 将编译 SQLite 查询到字节码,使用 sqlite_bind_int 绑定参数到 SQLite 语句。
为了防止使用 GCD 创建过多的线程,解决方法是创建串行队列, 或者使用带有最大并发数限制的 NSOperationQueue。
线程安全:UIKit只能在主线程执行,除了 UIGraphics、UIBezierPath 之外,UIImage、CG、CA、Foundation 都不能从两个线程同时访问。
不要在主线程执行磁盘、网络、Lock 或者 dispatch_sync、发送消息给其他线程等操作。
参考文章
以上是关于iOS-APP启动完成过程,以及耗时优化的主要内容,如果未能解决你的问题,请参考以下文章