iOS:APP启动过程及优化方案

大家好!我是OB!
正文开始前,先说说有的没的!

一、静态库和动态库

静态库动态库
格式.a 和 .framework(Mach-O Type为 Static Library).tbd(以前是.dylib) 和 .framework(Mach-O Type为 Dynamic Library)
链接时被完整的复制到可执行文件中,多次使用就会多份拷贝不复制,程序运行时由系统动态加载到内存,系统只加载一次,多个程序共用(如系统的UIKit.framework等),节省内存。
优点1:编译后的执行程序不需要外部的函数库支持,因为所有使用的函数已经被编译进去了。相对于动态库,执行效率更高1:动态库在编译的时候,并没有被编译近目标代码,你的程序执行到相关函数是才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。2:动态函数库的改变并不影响程勋,所以动态函数库的升级比较方便
缺点1:利用静态函数库编译成的文件比较大,因为整个函数库所有的数据都会被整合进目标代码中。2:静态函数库改变了,那么你的程序必须重新编译1:程序执行到相关函数时才进行调用,程序运行环境中必须提供相应的库 ,2:因为不是预先加载所以在链接函数的时候需要做大量的工作,相对来说比较耗时

二、Mach-O文件

Mach-O为Mach Object文件格式的缩写,它是一种用于可执行文件,目标代码,动态库,内核转储的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性,并提升了符号表中信息的访问速度。

mach
App可执行文件.o和系统dylib(动态链接库)都是Mach-O文件。

三、APP启动过程

进入主题:当我们点击icon到第一个页面展示完成,发生了什么样的过程?这个过程就是APP启动过程,也就是冷启动!

APP启动总时间 = mian()之前 + main() 之后;

1:main()函数之前

程序要想运行,必须要有对应的运行环境,那么这个过程就是搭建程序运行的环境。
App开始启动后,系统的dynomic loader首先加载可执行文件(.o文件),然后 dyld从可执行文件的依赖开始, 递归加载所有的依赖动态库(Dylib)。

dylddynomic loader动态链接器,作用是加载一个进程所需要的可执行文件,dyld是开源的

第一步:加载动态库

动态库包含:用到的所有系统 framework,以及一些libxxx的库,比如runtime的运行库:libobjc,运行GCD的库:libdispatch()。

加载动态库其实就是加载动态库的mach-o文件,主要加载过程如下:

  1. 找到动态库的mach-o文件
  2. 打开并验证文件
  3. 在系统注册文件
  4. 调用对应的各种启动函数

由于该过程也是需要时间处理的,因此可以如下优化:

  1. 不用或者少用非系统库
  2. 合并非系统库
  3. 使用静态库,只用到库的单一功能可以写成代码放入程序

总之:dyld 将 image(镜像:各种Mach-O文件) 加载到内存后,dyld 会通知Runtime进行下一步的处理

第二步:Runtime阶段

Runtime收到dyld的通知,就开始工作调用_init_objc()函数。开始解析内存中的images(镜像)

  • 注册Objc类 (class registration),初始化类,元类对象
  • 把category中的方法插入主类方法列表 (category registration)
  • 调用Objc的+load()函数
  • C++的初始化方法
  • 非基本类型的C++静态全局变量的创建(通常是类或结构体)

可执行文件和动态库中所有的符号(Class,Protocol,Selector,IMP,…)就会加载到内存中。

runtime处理完成后,runtime 的那些方法(动态添加 Class、swizzle 才能生效)。

由此可以做如下优化:

  1. Objc类数量和 selector数量越少越好,删除无用(没用到)的类和方法
  2. 减少C++虚函数数量
  3. 使用swift stuct:减少符号的数量

通过对Mach-O文件的了解,可以知道__TEXT:__objc_methname:中包含了代码中的所有方法,而__DATA__objc_selrefs中则包含了所有被使用的方法的引用,通过取两个集合的差集就可以得到所有未被使用的代码

总结一下:对于main()调用之前我们可以如下优化:

  1. 少用framework,是否可以合并非系统库,因为动态链接比较耗时
  2. check framework应当设为optional和required,如果该framework在当前App支持的所有iOS系统版本都存在,那么就设为required,否则就设为optional,因为optional会有些额外的检查
  3. NSObject类数量和 selector数量越少越好,删除无用(没用到)的类和方法
  4. +load()方法尽量延后调用,或者放在initialize()方法中执行
  5. 尽量不要用C++虚函数(虚函数指针与虚函数表。其中创建虚函数表有开销)

虚函数:通过基类指针只能访问派生类的成员变量,但是不能访问派生类的成员函数。虚函数virtual 可以访问。但是virtual需要用到虚函数指针与虚函数表,而创建虚函数表有开销。

2:main()之后

main()之后 主要工作就是初始化必要的服务,显示首页内容等。主要优化
Application:didFinishLaunchingWithOptions:方法

- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOption {
	//code ...
}

此时优化的方向主要是业务逻辑的优化

  1. 一些与UI展示无关的业务做延迟加载,比如各种认证,检测,注册等
  2. NSUserDefaults,FMDB等数据是否可以延后或者分开读取

四、共享缓存(shared cache)技术

dyld加载时,为了优化程序启动,启用了共享缓存(shared cache)技术。共享缓存会在进程启动时被dyld映射到内存中,之后,当任何Mach-O镜像加载时,dyld首先会检查该Mach-O镜像与所需的动态库是否在共享缓存中,如果存在,则直接将它在共享内存中的内存地址映射到进程的内存地址空间。在程序依赖的系统动态库很多的情况下,这种做法对程序启动性能是有明显提升的。

1:热启动

如果程序刚刚被运行过,那么程序的代码会被dyld缓存,所以当我们kill这个APP时,再次重启加载时间也会相对快一点,也就是热启动;

2:冷启动

长时间没有启动或者当前dyld的缓存已经被其他应用占据,那么又变成冷启动了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值