App启动分析与优化策略

一、 启动过程分析

1、解析Info.plist

a.加载相关信息,例如闪屏

b.沙箱建立、权限检查

2、Mach-O加载

Mach-O 文件:我们写的程序想要跑起来,肯定它的可执行文件格式要被操作系统所理解。比如ELF是Linux下的可执行文件格式,那么对于OS X / iOS来说,Mach-O 是其可执行文件格式。

Mach-O格式主要包括以下几种文件类型:

Executable:应用的主要二进制

Dylib:动态链接库

Bundle:不能被链接,只能在运行时使用dlopen加载

Image:包含Executable、Dylib和Bundle

Framework:包含Dylib、资源文件和头文件的文件夹

如果是胖二进制文件,寻找合适当前CPU类别的部分

加载所有依赖的Mach-O文件(递归调用Mach-O加载的方法)

定位内部、外部指针引用,例如字符串、函数等

执行声明为__attribute__((constructor))的C函数

加载类扩展(Category)中的方法

C++静态对象加载、调用ObjC的 +load 函数

3、程序执行

调用main()

调用UIApplicationMain()

调用applicationWillFinishLaunching

二、测量App的启动时间

iOS App的启动有两种方式:冷启动和热启动,下面对这两种启动方式进行简单介绍。

冷启动:App第一次启动或者是被上滑Kill掉之后,再次从点击App应用图标到App完全启动显示主页面为止的过程成为冷启动。

热启动:当用户从当前App按下HOME键退出后,系统并不会立即kill掉App的进程,还会继续后台执行一段时间(至于什么时候会被kill,完全取决于系统内存等资源的使用情况,理想情况下可以保持后台运行15min)。然后当用户再次点击App图标时,会很快加载到上次停留的页面,几乎不需要进行资源载入,这种情况下的启动成为热启动。

下面我们对于App启动时间的测量只讨论在冷启动情形下。根据苹果官方介绍,iOS应用的启动分为两个阶段:pre-main和main,所以App启动的总时间为:Total Time = pre-main Time + main Time。

Pre-main 阶段

Pre-mian 阶段即main()函数被调用之前的加载时间,包括dylib动态库的加载、Mach-O文件加载、Rebase/Bining、Objective-C Runtime 加载等。该过程如下图所示:

main 阶段

main 阶段指的是从调用main()函数开始,调用UIApplicationMain()创建UIApplication,AppDelegate,直到回调App代理方法applicationDidBecomeActive:为止,其中包括application:didFinishLaunchingWithOptions方法中创建keyWindow、三方sdk和其他初始化项所消耗的时间,也包含RootViewController 的 ChildViewController的view完全显示出来所花费的时间。该过程如下图所示:

main 阶段时间测量

那么对于pre-main 和 main 启动阶段的耗时如何测量呢?苹果已为我们提供了一个简单易用的测试方法:在Xcode中选中项目的Scheme→ Edit Scheme...→,然后选择Run→Arguments→Environment Variables中添加Name = DYLD_PRINT_STATISTICSvalue,Value = 1 的环境变量。如下图所示:

然后重新运行项目,注意控制台下面的打印输出,格式为:

Totalpre-main time:655.68milliseconds (100.0%)

dylib loading time:205.67milliseconds (31.3%)

rebase/binding time:320.95milliseconds (48.9%)

ObjCsetup time:63.07milliseconds (9.6%)

initializer time:65.85milliseconds (10.0%)

          slowest intializers :

libSystem.B.dylib :3.43milliseconds (0.5%)

libMainThreadChecker.dylib :15.59milliseconds (2.3%)

century :76.56milliseconds (11.6%)


Totalpre-main time:  表示pre-main阶段总的时间消耗为655.68毫秒

dylib loading time: 动态库加载时间

rebase/binding time: 重定位指针指向/指向镜像外部内容 所需时间

ObjCsetup time:ObjC初始化时间

initializer time: 其他初始化时间的总和

slowest intializers : 最慢的三个初始化分别是libSystem.B.dylib、libMainThreadChecker.dylib、century(项目名称)

main 阶段时间测量

由于main阶段涉及到大量的自定义代码,app主体页面的初始化、前期配置、三方库初始化等大量逻辑代理,iOS系统没有直接的测量方式,所以只能通过打点的方式计算启动开始到启动结束的时间差即可。例如下面示例代码:

// main.m 文件

#import"AppDelegate.h"

externCFAbsoluteTimeStartTime;

intmain(intargc,char* argv[]) {

// 记录开始时间

StartTime =CFAbsoluteTimeGetCurrent();

@autoreleasepool{

returnUIApplicationMain(argc, argv,nil,NSStringFromClass([AppDelegateclass]));

    }

}

// AppDelegate.m 文件

CFAbsoluteTimeStartTime;

- (void)applicationDidBecomeActive:(UIApplication*)application {

dispatch_async(dispatch_get_main_queue(), ^{

NSUIntegermilliseconds = (NSUInteger)((CFAbsoluteTimeGetCurrent() - StartTime) *1000);

NSLog(@"Loading done in %lu ms", milliseconds);

    });

}


影响启动性能的因素

接下来我们讨论在App启动的pre-main阶段和mian阶段,影响启动性能的多种因素。

Pre-main 阶段

动态库加载越多,启动越慢。

ObjC类越多,启动越慢

C的constructor函数越多,启动越慢

C++静态对象越多,启动越慢

ObjC的+load方法使用越多,启动越慢

我们尽量不要写__attribute__((constructor))的C函数,也尽量不要用到C++的静态对象;至于ObjC的+load方法,似乎大家已经习惯不用它了。任何情况下,能用dispatch_once()来完成的,就尽量不要用到以上的方法。

main 阶段

执行main()函数的耗时

执行application:didFinishLaunchingWithOptions的耗时

rootViewController及其childViewController的加载、view及其subviews的加载

一般的App的主体UI架构是,UITabbarViewController 作为 keyWindow的根控制器,然后包含多个ChildViewController作为每个tab的根控制器。然而在didFinishLaunchingWithOptions代理方法中各控制器的初始化顺序是怎样的呢?

答案是:

-[MQQTabBarController viewDidLoad]

-[MQQTab1ViewController viewDidLoad]

-[AppDelegate application:didFinishLaunchingWithOptions:]

-[MQQTab2ViewController viewDidLoad] (点击了第二个tab之后加载)

-[MQQTab3ViewController viewDidLoad] (点击了第三个tab之后加载)

从上面的加载过程可以看出,在main()方法之后的启动中,只要是对页面的渲染,要减少这个阶段的耗时需要在viewDidLoad方法中做优化,减少不必要的逻辑,视图尽量使用懒加载,使用异步方式加载网络数据。

三、启动时间优化

苹果建议在400ms内完成pre-main 阶段的启动,App整体的启动时间不能超过20s,否则系统会被kill掉进程。

由于每个App的体量和类型不一样,对应启动过程中配置的逻辑处理复杂程度也是皆不相同,所有没有一个标准的时间来衡量启动时间是否达到最优。但是可以从用户体验上下功夫预加载占位视图,数据缓存等都可以营造出加载速度提升的感觉。

下面从实践的角度分析可优化的部分:

pre-main 阶段优化

1、移除不需要的动态库framework

2、移除不需要的类、未使用的变量、方法等

对应项目中大量的文件,可以使用工具快速扫描整个项目找出未使用的类、变量、或方法。这里推荐一个开源工具 Fui 能准确的完成此任务,不足之处在于它处理不了动态库或静态库中提供的类,和C++ 的类模板。

Fui使用方法:

在终端中cd到项目根目录,然后执行fui find,就可以得到一个列表,可根据这个列表在Xcode中手动检查并删除无用代码。

3、合并功能类似的类和扩展(Category)

4、+load方法中做的事情可以使用dispatch_once()来代替,因为main()方法被调用之前就会加载+load方法,会影响pre-main阶段启动事件。

5、压缩资源图片(对要求不高的图片可做压缩处理)

main 阶段优化

1、优化application: didFinishLaunchingWithOptions:内部逻辑

各种业务请求配置更新,多个配置更新可合并为单个请求,部分业务的配置延后更新。

对于新版本引导、广告闪屏逻辑,需要重构逻辑清晰

数据迁移,对太旧的无用的逻辑可以删除,不需要立即处理的数据,改为后台加载

2、优化rootViewController加载:在启动过程中只加载tabbarVc,主Vc即可,而且主Vc中的ViewDidLoad方法中也只加载需要立即显示出来的view,其他视图均使用懒加载,数据进行异步加载。


文章转载:https://www.dazhuanlan.com/2020/01/04/5e1055a4e07fa/?__cf_chl_jschl_tk__=7750f38bebb1f21fdaddcddca0415ee71d31c736-1614321013-0-AWpmiwwC2bq_8PHZ6dbnAI7dbzqLC36vJnCCmhi6ThuE9vMnd3LM67aZyDLR_NJL9OD6mRUWHOQyh3g4j0Pu2gfxG2bqnAUKHjusvzyCt9SgFPr_BvqiVVboKbJT2CnMKE17X9TXh29kBG4pUgxySpMoZiroz9gDZaKkCapAOGs88StJKu4rDYYjwNwCfv5apw7KDW2yUwjsu-8JFscQ3j0KlbepXj_f87G1Cq2LI0JosaCI1xKUmw1csldKhrqcDOmp-hMtRnt6tF_8dQ1ggjBaRUb0qne-6PNV4HeCmgyrGT4o2mjAJDBLu8aaGNuROH0camvmKwRQHg5_L0zQT0I

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容