IOS APP启动优化

前言 作为程序猿来说,“性能优化”是我们都很熟悉的词,也是我们需要不断努?以及持续进?的事情;其实优化 是?个很?的课题,因为细分来说的话有??????种优化?向 ,但是切忌在实际开发过程中不能盲?的 为了优化?优化,这样有时可能会造成适得其反的负效果,需要我们根据实际场景以及业务需求进?合理优 化。接下来进?正题,本?将会以iOS App的启动优化为展开点进?探讨。
启动流程 iOS App 的启动我们都知道分为pre-main main() 两个阶段,并且在这两个阶段中,系统会进??系列的加载操作,过程如下:
1、 pre-main阶段
1、加载应?的可执??件
2、加载dyld动态连接器
3、dyld递归加载应?所有依赖的动态链接库dylib
2、 main()阶段
1、dyld调? main()
2、调? UIApplicationMain()
3、调? applicationWillFinishLaunching
4、调? didFinishLaunchingWithOptions
阶段优化项 1、 pre-main阶段
针对 pre-main 阶段做优化时,我们需要先详细了解其加载过程,这个可以在2016年WWDC 的Optimizing App Startup Time 中详细了解到相关材料
1.1 Load dylibs IOS APP启动优化
文章图片

这?阶段dyld会分析应?依赖的 dylib (xcode7以后.dylib已改为名.tbd),找到其 mach-o ?件,打开和读取这些?件并验证其有效性,接着会找到代码签名注册到内核,最后对 dylib 的每?个segment 调? mmap() 。不过这?的 dylib ?部分都是系统库,不需要我们去做额外的优化。
优化结论:
1、尽量不使?内嵌的dylib,从?避免增加 Load dylibs开销
2、合并已有的dylib和使?静态库(static archives),减少dylib的使?个数
3、懒加载dylib,但是要注意dlopen()可能造成?些问题,且实际上懒加载做的?作会更多
1.2 Rebase/Bind IOS APP启动优化
文章图片

dylib的加载过程中,系统为了安全考虑,引?了ASLR (Address Space Layout Randomization)技术和代码签名。由于ASLR的存在,镜像(Image,包括可执??件、dylibbundle)会在随机的地址上加载,和之前指针指向的地址(preferred_address)会有?个偏差(slide), dyld需要修正这个偏差,来指向正确的地址。Rebase在前,Bind在后,Rebase做的是将镜像读?内存,修正镜像内部的指针,性能消耗主要在IO。 Bind做的是查询符号表,设置指向镜像外部的指针,性能消耗主要在CPU计算。
在此过程中,我们需要注意的是尽量减少指针数量,?如:
1、减少ObjC类(class)、?法(selector)、分类(category)的数量
2、减少C++虚函数的的数量(创建虚函数表有开销)
3、使? Swift struct (内部做了优化,符号数量更少)
1.3 Objc setup ?部分ObjC初始化?作已经在Rebase/Bind阶段做完了,这?步dyld会注册所有声明过的ObjC类,将分类插 ?到类的?法列表?,再检查每个selector的唯?性。
在这?步倒没什么优化可做的, Rebase/Bind阶段优化好了,这?步的耗时也会减少。
IOS APP启动优化
文章图片

1.4 Initializers 在这?阶段,dyld开始运?程序的初始化函数,调?每个Objc类和分类的+load?法,调?C/C++ 中的构造器 函数(?attribute((constructor))修饰的函数),和创建?基本类型的C++静态全局变量。Initializers阶段执? 完后,dyld开始调?main()函数。
优化结论:
1、少在类的+load?法?做事情,尽量把这些事情推迟到+initiailize
2、减少构造器函数个数,在构造器函数?少做些事情
3、减少构造器函数个数,在构造器函数?少做些事情
main()阶段
在这?阶段?,主要优化重点放在 SDK初始化、业务?具注册、整体didFinishLaunchingWithOptions ?法中,因为我们的?些第三? app ?格配置、启动引导?显示状态逻辑、版本更新逻辑等等基本?都会在这?进?,如果这部分逻辑没有做好优化梳理,随着业务不断拓展,臃肿的业务逻辑会直接导致启动时 间加?。
优化结论:
在满?业务需求的前提下,尽量减少 didFinishLaunchingWithOptions ?法在主线程中的事件处理逻辑, ?如:
1、根据实际业务状况,梳理各个??/三?库,找到可以延迟加载的库,做延迟加载处理,?如放到??控制器 的viewDidAppear?法?。
2、梳理业务逻辑,把可以延迟执?的逻辑,做延迟执?处理。?如检查新版本、注册推送通知等逻辑
3、避免进??些复杂/多余的计算逻辑,这类逻辑尽量进?异步延迟处理
4、避免在??控制器的viewDidLoadviewWillAppear做太多容易阻塞主线程的事情,这2个?法执?完,??控制器才能显示
场景补充
另外,在我们实际开发过程中,很多项?的??控制器都会有?些后台可配、较为丰富的结构或者推荐数据 进?展示,?且我们的??展示速度通常也会被纳?启动优化的?部分,其实对于这种类型的优化,如果我 们还只是?传统的api -> data -> UI ?式进?的话,就很难有明显的改善空间,因为?户的?络状态 并不是可控项,如果不做其他处理的话,那在很多场景下对?户来说,即使我们放上?些占位图,展示的样 式也是很不友好的,毕竟??控制器对?户的第?视觉冲击影响还是?较?的。
对于这种场景下的优化来说,?般我们可以采取 Local + Network + Update 的?式在?定程度上优化??加载速度即:
1、 app更新过程中,?先进?本地内嵌处理逻辑,内嵌??数据结构( localDataBase)、内嵌??样式所需资源( localStorage
2、在安装启动之后,对本地与线上数据更新记录进?对?,检测是否需要更新本地内嵌数据结构
3、检测到有需要更新的数据时,才会对指定结构进?静默更新,并且同步更新本地数据结构
这样做的好处是:
1、??数据直接从本地加载,减少?络数据等待时间
2、仅检测数据key值变化,?数据量对?定向更新结构,减少api数据交互频次及数据包体积
3、能够保证??对于?户来说会?直处于?个友好的展示状态
当然这种也并不是唯?的应对?式,?且也并?对所有场景都适?,只是提供?种思路?已,还是需要根据 项?的实际场景选择适合的优化?案。
统计时长 另外如果在开发过程中,我们想直观的查看 app 启动期间,各阶段的耗时情况,也可以在 的 edit scheme 设置添加 DYLD_PRINT_STATISTICS 为1 ,打印启动时?,例如
IOS APP启动优化
文章图片

优化前启动时?:
IOS APP启动优化
文章图片

优化后启动时?:
IOS APP启动优化
文章图片

当然,这些log我们仅仅只能在开发调试阶段查看打印,那么在实际项?中,我们需要对线上项?的启动数据 进?监控,以便及时的定位和优化那些影响 app 启动时?的环节,这时我们应该怎样更好的处理呢?
当然我们可以通过服务器埋点上报的?式??统计分析,不过这样?来会发现我们的统计成本就会??增加,?且结果分析也会变得不那么灵活。所以这?推荐?种简单的监控?式,那就是友盟的 U-APM 应能性 能监控SDK ,只需要我们进?简单的pod集成之后,便可根据我们的实际需要进??动或者?动监控启动数 据,详情可以参考 U-APM, 并且为了?便我们对数据进?分析,友盟后台已经根据这些数据帮我们绘制出 了对应的分布图,我们可以??了然的得出启动耗时分布、启动类型占?等等,如图:
IOS APP启动优化
文章图片

IOS APP启动优化
文章图片

除此之外,我们还可以通过SDK进?崩溃分析、ANR分析、监控告警、卡顿分析、内存分析等等诸多功能, 有了 U-APM 这个监控平台,其实在实际开发过程中很?程度的提升了我们对线上 app 的优化分析效率。
当然本?的介绍也只是?较浅显的优化项,仅供参考以及思路引导,优化之路任重?道远,还需要我们不断 的去探索、发现、提?。不过最后还是要提醒?句:在实际项?开发过程中,不要为了优化?优化,要根据 项?情况有针对性的进?优化。
参考 【IOS APP启动优化】探秘 Mach-O ?件
iOS 底层 - 从头梳理 dyld 加载流程
iOS app启动 - dyld加载App流程
wwdc2016optimizingappstartuptime.pdf

    推荐阅读