app启动的快慢
有可能决定着用户是否继续使用这个app,app启动优化是程序优化比较重要的一个环节,今天来讨论一下app的启动优化。
##冷启动:
app启动前,它的进程不是系统里,需要系统新创建一个进程分配给它启动,这是一次完整的启动过程。(从无到有)
##热启动:
app冷启动后用户切到后台,app进程还在系统里面,用户重新启动进入app的过程,这个过程做的事情非常少。(后台切前台)
app启动分为三个阶段,看看他们分别做了什么: 1.main函数执行前。(阶段:点击app触发)
- 可执行文件加载。
- 动态库加载。
- +load()方法、constructor静态全局变量。
第一阶段的优化:
- 减少动态库加载,可以把多个动态库合并成一个(最多可以6个合成1个)
- 减少加载启动后不使用的类和方法
- +load()方法内容放在首屏渲染完成执行 或者用+initialize()代替,+load()方法一个耗时4ms
- 控制c++全局变量的数量
2.main函数执行后。(main函数执行开始到appDelegate的didFinishLauchingWithOptions首屏渲染方法执行完成)
- 首屏相关基础库初始化。
- 首屏相关业务数据读取和处理。
- 首屏渲染。
3.首屏渲染完成后。(首屏渲染方法完成后到didFinishLauchingWithOptions作用域结束)
- 其他业务基础功能初始化
- 其他业务服务初始化
- 监听注册
优化手段: 功能级别的启动优化: 在第二阶段,main函数开始执行到首屏渲染完成前只处理首屏相关的业务,其他的非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染后去做。 方法级别的启动优化: 优先要检测主线程上耗时的方法。
##第一种方式 是用Time Profiler定时抓取主线程方法调用堆栈,计算一段时间里各个方法的耗时。 成本不高,快速集成,但是精确度不是很高,一般设置0.01秒。 时间设置得短(0.002秒)能监听到所有方法,但是整体耗时不准确。 时间设置0.01秒,单个方法耗时不准,但对整体耗时影响小。
##第二种方式 对##objc_msgSend方法进行hook掌握所有方法的执行耗时。 hook方法意思是,在原方法开始执行时换成执行你自定义的方法,或者在原有方法执行前后执行你指定的方法。 hook objc_msgSend优点是非常精确, 但是只能hook oc的方法,对于c和block,使用libffi的ffi_call来达成,但缺点是编写维护相关工具门槛高。
为什么hook了objc_msgSend方法,就可以hook全部oc方法? oc里每个对象都指向一个类,每个类都有一个方法列表,方法列表里每个方法都由selector、函数指针和metadata组成的。 objc_msgSend就是在运行时根据对象和方法的selector去找对应的函数指针,然后执行。 objc_msgSend是oc方法的必经之路,能够控制所有的oc方法。由于调用次数最高 所以objc_msgSend是汇编写的,性能优化达到极致。
objc_msgSend方法执行逻辑:先获取对象对应类的信息,再获取方法的缓存,根据方法的selector查找函数指针,经过异常错误处理后,最后跳到对应函数的实现。
怎么hook objc_msgSend方法? Facebook开源的fishhook库可以在ios运行的Mach-O二进制文件动态绑定符号。通过重新绑定符号,实现对c方法的hook。