iOS底层探索30、启动优化分析

引言

APP 启动,对用户而言,是从点击 APP 开始,到看到 APP 首页展现的过程。

冷启动:针对APP,内存中不包含信息,需要将资源从磁盘读取到内存中。
杀掉APP并不一定进入冷启动(当然重启时可以的)。
热启动:进程的数据仍然在,在启动过程中不用再次加入内存。

启动,通过main函数将其分为2个大的模块:
1.main函数之前,通过系统监测启动耗时;

  1. main函数之后,我们自己打点进行时间检测。 - 根据具体业务进行操作
    2.1 举例:在main函数和首屏渲染出现之前,即ViewDidLoad()中分别打点,可同时记录此阶段中所有方法的时间消耗,找出耗时的进行优化。

一、启动时间 - pre-main

1、启动耗时检测分析

如下图,工程配置添加DYLD_PRINT_STATISTICS

image.png

运行工程,输出如下:

Total pre-main time: 184.72 milliseconds (100.0%)
         dylib loading time: 147.66 milliseconds (79.9%) - 动态库的耗时
        rebase/binding time: 126687488.9 seconds (107600740.5%) - 偏移修正/绑定
            ObjC setup time:  11.20 milliseconds (6.0%) - OC 类的注册
           initializer time:  36.96 milliseconds (20.0%) - 初始化耗时:执行 load 和 constructor构造函数 的耗时
           slowest intializers :// 最慢的几项
             libSystem.B.dylib :   4.66 milliseconds (2.5%)
   libBacktraceRecording.dylib :   5.24 milliseconds (2.8%)
    libMainThreadChecker.dylib :  22.02 milliseconds (11.9%)

rebase/binding time -

1)rebase:偏移修正

每个应用的二进制文件,我们所有方法函数调用编译时二进制文件中都有他们的偏移地址。以函数func01()为例,假定函数地址为0x0001;
运行时,地址将会发生改变,ASLR会所及生成一个值,插入在二进制文件的开头。假定生成的随机值是0x1000,那么此时,func01()的地址就是:0x1000 + 0x0001 = 0x1001,即方法的真实地址!

总结:
地址偏移值 + 随机值 = 运行时刻的真实地址

即:
ASLR + 文件本地的偏移值 = 修正值,此过程的耗时 --> 偏移修正耗时

ASLR - 安全机制

2)binding:符号绑定

绑定耗时。
NSLog为例,NSLog的地址我们是无法直接知道的,它在Foundation框架中 - 属于外部动态库。

*编译时NSLog的真实地址是拿不到的,so,在MachO文件中会创建一个NSLog的符号(它存在在 MachO文件的数据段中),此时指向一个随机 或 固定的无意义的值。

*运行时会进行符号绑定binding,将符号所指向的地址关联真正NSLog的地址
--> 此关联过程即绑定。在内存中进行绑定的.

MachO文件本身是在磁盘中的,运行时从磁盘加载到内存中,从磁盘到内存的过程像一个copy,就叫做image镜像。即一个可执行文件从磁盘加载到内存便是一个镜像文件,而当镜像文件加载到内存后会进行绑定。此处理由 dyld进行。
简单来说,此过程:dyld加载进程时,根据NSLog的符号依赖的库以及要使用的是库中的NSLog,找到所在库及库中NSLog的所在地址,然后进行绑定。即:给符号绑定真正地址的过程。

2、启动耗时优化建议

1)动态库

动态库的载入要耗时且动态库内部又可能有自身的一些关联依赖要进行搜寻查找的过程耗时。
因此针对动态库的耗时优化,即是 减少外部动态库的使用,系统的库都是做了高速优化的暂不考虑,主要针对外部添加的动态库,苹果官方给出建议:添加自定义的外部动态库最好不要超过6个,多于6个便要考虑合并动态库。

2)OC类

减少OC类,它带来的耗时其实较少,优化要考虑业务实际场景。这里相较来说swift会比OC更快速。

  • 项目中废弃的类要干掉,它会加载占用时间;

3)初始化耗时 - 执行 load 和构造函数的耗时

延迟,没必要在 load()constructor构造函数 中做的事情进行延迟:

  1. 延迟到initialize()去做;
  2. 延迟到main之后去做。

二、main 到 首屏展现

我们通过新建一个工程,编写下面简单代码进行分析。
创建MyTabBarCtr,将其作为根视图:

- (void)viewDidLoad {

    NSLog(@"%s",__func__);

    [super viewDidLoad];
    // Do any additional setup after loading the view.
        
    UIViewController *vc01 = [[MyVC_01 alloc] init];// 红背景
    vc01.tabBarItem.title = @"首页";
    [self addChildViewController:vc01];
    
    UIViewController *vc02 = [[MyVC_02 alloc] init];// 绿背景
    vc02.tabBarItem.title = @"二页";
    [self addChildViewController:vc02];
    
    UIViewController *vc03 = [[MyVC_03 alloc] init];// 蓝背景
    vc03.tabBarItem.title = @"三页";
    [self addChildViewController:vc03];
}

运行程序,程序显示首页前,所做事务顺序如下:

image.png

我们将断点打在sceneWillEnterForeground:APP 将要从后台走向前台,重新运行工程:

image.png

由上面结果可看到,在首屏展示之前,其viewDidLoad是要首先处理完成的。
这里我们可以想到若直接在TabBarController首屏viewDidLoad中处理较多绘制渲染、业务逻辑处理等,那么用户看到首页的时间也会加长 --> 导致启动速度慢。
验证:
我们分别在VC01MyTabBarCtrviewDidLoad中让其sleep(5),运行工程,首屏的展示均会比添加sleep之前多至少 5 秒!我们把sleep搁置到异步子线程去处理,此5秒便省下来了!!

优化建议:

1、针对启动时的必要耗时加载考虑用多线程,在启动时刻可去尽力发挥CPU的性能,这一下下而已其实不必考虑耗时;
2、UI框架的优化,例如在RootViewController中不要做不必要的业务处理,并避免在其中处理一些不需立马展示的子VC的初始化;
3、首屏VC的优化,业务按需处理,避免大量耗时。可对页面进行预展示,数据来了之后才显示实际数据内容;
4、异步,开子线程是个好东西,当然也要根据也无需求合理使用。

其他一个建议
图片资源的优化,启动过程中,难免会有图片的加载处理,且实际业务中图片一般会采用清晰度高的,若图片很大很多,IO量就会增大,自然会影响到启动速度。
虽然 Xcode 在编译APP时,已经对我们需要打包进APP里的图片做过一层压缩处理了,但 Xcode 压缩处理相对来说比较轻微保守。
so:图片的无损压缩不失为一个方法。但目前我对其还没有具体了解,日后探究。

针对启动优化的总结:

  1. 自定义外部动态库的添加遵守苹果官方建议,不要超 6 个;
  2. 减少不必要的OC类,开发总适当考虑废弃去除不再使用的业务类;
  3. load 和 构造函数尽量少用,可将其推迟到initialize()
  4. 启动时根据实际场景将耗时放入子线程处理;
  5. RootViewController中避免不必要的业务处理和子VC的初始化;
  6. 首屏的预展示;
  7. 图片资源的压缩。

暂时 以上。
后面文章将继续针对启动优化进行分析。

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

推荐阅读更多精彩内容