重学iOS系列之APP启动(一)流程

导读

本文将带您了解iOS APP从点击图标到显示画面的大致过程,本文只不深入解析相关源码,相关源码解析会在后续的章节详细讲解。

我们为什么要了解APP启动流程?

在开发过程中,随着业务的不断增加,代码量也不断增加,APP体量越来越大,APP的启动速度越来越慢,启动速度慢导致用户体验差,那么新需求就来了,降低APP的启动时间!

如何降低APP的启动时间呢?

这个问题可以拆分成

1、APP启动时间降低到多少才能让用户有飞一般的体验

2、如何降低APP启动时间?

在回答问题之前,我们先了解下怎么测量启动时间:

在Xcode菜单栏:Product->Scheme->Edit Scheme->Environment Variables

设置:key:DYLD_PRINT_STATISTICS    value:1

然后运行工程,就会将启动时间以及过程耗时打印出来了。

当然了,打印的时间都是分模块的,如果要具体要每个函数的调用时间,就需要借助其他的工具,比如自己写一套hook objc_msgSend(),这个难度非常大,简单点的方式就是借助Xcode自带的Instrument工具,具体怎么操作就不展开讲解,有兴趣的同学可以自己找找资料。

回到之前的问题上来,先回答第一个问题:

在WWDC2016上苹果官方给出的APP启动时间建议是低于400ms,这样可以确保在 Springboard 的应用启动动画结束前,你的应用就做好准备可以使用了。而且如果启动时间超过20秒则会被系统杀死,意味着你的APP将启动失败。

Apple suggest to aim for a total app launch time of under 400ms and you must do it in less than 20 seconds or the system will kill your app

问题1已经解决,那么我们回到问题2。

App启动一般分两种,冷启动和热启动。

热启动是指 ,App 在冷启动后用户将 App 退后台,在 App 的进程还在系统里的情况 下,用户重新启动进入 App 的过程,这个过程做的事情非常少。(即有数据缓存为热启动)

冷启动是指, App 点击启动前,它的进程不在系统里,需要系统新创建一个进程分配给 它启动的情况。这是一次完整的启动过程。(重启设备后,任意APP的启动都为冷启动)

一般情况下,热启动由于有缓存的原因,速度都非常快,肯定是低于400ms的,这种情况我们不需要优化。

OK,重点来了,冷启动!

现在问题2变成了:如何降低冷启动的时间?

不知道!game over结束!


身为一个求生欲非常强的猿,我们肯定不能就这样回答结束。

想解决问题,就要先了解问题的本质。进入正题,启动流程!

我们先用Xcode新建一个Single View App工程,取名Launch_Demo,在工程中能接触到最开始执行的代码在main.m文件中的main()函数,我们先在main函数中打个断点,然后启动模拟器:



修改debug选项,用于显示汇编指令代码,然后点击左侧的start


可以看到图中的汇编代码是断在一个libdyld.dylib动态库的一个start()函数中,那libdyld.dylib这又是什么呢?

在这之前我们先了解下什么是dyld,dyld(dynamic link editor)是苹果系统内核中的动态连接器,是苹果系统的重要组成部分,负责加载可执行文件到内存中。而且它是开源的,任何人可以通过苹果官网下载它的源码来阅读理解它的运作方式,了解系统加载动态库的细节,dyld下载地址:http://opensource.apple.com/tarballs/dyld

我们先把源码下载下来,目前最新版本是dyld-852.2

打开dyld源码工程,全局搜索_dyld_start,然后在dyldStartup.s这个汇编文件中找到了具体的实现,代码包含了各种架构的实现,我们只需要关注iOS的实现,摘取了arm64架构的实现如下:


可以看到有一行代码

// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)

bl __ZN13dyldbootstrap5startEPKN5dyld311MachOLoadedEiPPKcS3_Pm

从注释我们可以知道这句代码就是调用了start()函数,咦,这不就是上面我们断点里的那个start()函数吗?

再全局搜索start(,结果非常多,我们找跟bootstrap有关系的,然后我们在dyldInitialization.cpp文件中找到函数的具体实现:


start()函数做了一堆初始化的事情,具体的先不管,看重点,return dyld::_main()

初始化工作完成后,此函数调用到了dyld::_main(),再将返回值传递给__dyld_start去调用真正的main()函数。

光标放到_main()的位置,右键选中Jump to Definition直接跳转到main()函数的具体实现。然后就懵逼了,900行代码的具体实现(心里一阵草泥马狂奔而过)。


代码太长了,我们找重点,一番查找,找到如下一段代码:


看注释:

// run all initializers (执行所有的初始化器)

initializeMainExecutable(); 

// notify any montoring proccesses that this process is about to enter main() (通知任何正在监听的进程,当前进程要进入main()函数了)

notifyMonitoringDyldMain();

很好,已经快要接近我们的目标了,继续往下读源码找重点:


这是什么?

看注释:find entry point for main executable (找到主程序的入口,就是我们的工程里的main()函数)。

目前都还是静态代码的分析,我们用一个demo来验证一下,我们都知道load()方法会在main之前调用,所以我们通过在load方法里打一个断点的方式追踪一下程序的调用堆栈。

打开之前创建的demo工程,在ViewController中新增一个+(void)Load方法,然后将断点打到方法里面,运行程序。


从图中可以看到,函数的调用顺序和我们分析的差不多,我们只分析到dyld::_main()函数,上图的很多函数调用都是_main()内部调用的。细心的同学会发现dyld最终调用了notifySingle,然后就进入libobjc.A.dylib的load_image函数了,我们熟悉的runtime就是在这个动态库里的,那么这部分是怎么跳转的呢?


看到这里,很多读者肯定会觉得很困惑,这个流程感觉什么都没有啊,没有干货啊,被大家熟知的runtime在哪启动的,类在哪加载的,category什么时候加载,load方法什么时候调用等等。

不要急,APP的启动是个非常复杂的过程,涉及到的源码非常多,不仅仅是dyld库被调用,还有上图的libobjc.A.dylib,以及还没有出现的libSystem.dylib libdispatch.dylib等。

我们目前了解到的还只是冰山一角,真正的想要完全吃透这一块是要有很多的知识积累,比如用户态和内核态,mach-o的一些知识,静态库与动态库相关知识,虚拟内存,共享缓存等等等等。

当然我们目前分析的都是基于用户态的启动过程,在内核态还有另外一套启动过程来启动dyld,由于iOS开发不涉及内核开发,所以不在此深入探究,有兴趣的同学可以自己找资料学习相关知识。

总结下今天的收获,目前我们了解到的调用流程是这样的:

_dyld_start --> dyldbootstrap::start()--> dyld::_main() -->_main()函数里一堆逻辑代码 -->主程序main()

中间省略的调用逻辑,最少包含了:

1、Xcode环境配置相关信息的检查

2、静态库、动态库的加载以及链接

3、runtime的初始化

4、类的初始化

5、catagory的初始化

。。。。。。。。。等等

这些流程将会分为几个小节进行详细的源码分析带领大家了解。


下一篇:重学iOS系列之APP启动-dyld(二)将带大家拉开dyld的神秘面纱,领略下苹果官方的开发是怎么编写一个app的启动逻辑的。

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

推荐阅读更多精彩内容