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、发送消息给其他线程等操作。

参考文章

App启动过慢,你可能想到的因素有哪些?

介绍下App启动的完成过程?

如何优化 App 的启动耗时?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值