iOS app是如何启动的

219 阅读4分钟

app启动分为冷启动,热启动

冷启动:app点击启动时,进程不在系统中

热启动:app在后台,进程还在系统中时,用户重新启动

启动步骤

一.main()函数执行前

1.加载dyld到App进程:

dyld,dynamic loader,作用是加载一个进程需要的image。

这个image不是图片的意思,它大概表示一个二进制文件(可执行文件或so文件),里面是被编译过的符号、代码等,所以imageLoader作用是将这些文件加载进内存,且每一个文件对应一个imageLoader实例来负责加载。

2.加载动态库:

dyld首先读取mach-o文件的header和load commands load commands里有可执行文件依赖的动态库,递归加载,缓存到dyld shared cache 可以通过命令行otool查看:

192:Desktop Leo$ otool -L demo 
demo:
	@rpath/PullToRefreshKit.framework/PullToRefreshKit (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1444.12.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	@rpath/libswiftCore.dylib (compatibility version 1.0.0, current version 900.0.65)
	@rpath/libswiftCoreAudio.dylib (compatibility version 1.0.0, current version 900.0.65)
	//...

3.Rebase和Bind:

为什么要rebase?

有两种技术保障应用的安全:ASLR和Code Sign ASLR:App被启动的时候,让起始地址随机,黑客不容易通过起始地址和偏移量找到函数地址

Code Sign:签名,hash算法获得摘要,用私钥加密,获得签名

mach-o文件中有很多符号,有指向当前mach-o的,也有指向其他dylib的, 运行的时候,dyld通过rebase修正指向当前mach-o的函数或变量的指针指向 bind修正指向外部的指针指向

4.初始化Objective C runtime:

OC是动态语言,所以执行main之前,需把类注册到全局table中 selector 唯一性检查 OC支持Category,初始化的时候也会把分类中的方法注册到对应的类中

5. Initializers:

+load方法 C/C++静态初始化对象和标记为__attribute__(constructor)的方法 使用__attribute__(constructor)可以保证该方法在main函数之前被调用 attribute((constructor)) static void beforeFunction() { printf("beforeFunction\n"); }

知识点:

Mach-O文件 Mach Object文件格式,用于可执行文件,目标代码,动态库的文件格式

header:可执行的CPU架构,x86,arm64 Load Commands :加载命令,包含文件组织架构和在虚拟内存中的布局 Data:数据,包含Load Commands 中需要的各个段的数据 可以使用MACHOView查看内容

dyld: dyld的全称是dynamic loader,它的作用是加载一个进程所需要的image,dyld是开源的。

Page fault: 在应用执行的时候,它被分配的逻辑地址空间都是可以访问的,当应用访问一个逻辑Page,而在对应的物理内存中并不存在的时候,这时候就发生了一次Page fault。当Page fault发生的时候,会中断当前的程序,在物理内存中寻找一个可用的Page,然后从磁盘中读取数据到物理内存,接着继续执行当前程序。 dyld2和dyld3的区别:

dyld2是纯粹的in-process,也就是在程序进程内执行的,也就意味着只有当应用程序被启动的时候,dyld2才能开始执行任务。 dyld3则是部分out-of-process,部分in-process。图中,虚线之上的部分是out-of-process的,在App下载安装和版本更新的时候会去执行

out-of-process会做如下事情:

分析Mach-o Headers

分析依赖的动态库

查找需要Rebase & Bind之类的符号

把上述结果写入缓存

二.main函数:

int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([SXAppDelegate class])); } }

UIApplicationMain函数做了什么事:

1.根据第三个参数创建UIApplication对象,nil时,即为@“UIApplication”

2.根据传入的代理名创建代理对象,这里为SXAppDelegate

3.main函数为什么不退出?

开启RunLoop,不开启的话,main执行完程序就结束了

注册两个Observer,所以不退出

第一个Observer监视事件kCFRunLoopEntry,回调为_wrapRunLoopWithAutoreleasePoolHandler,

回调内部调用_objc_autoreleasePoolPush()创建自动释放池,

第二个Observer监视两个事件:kCFRunLoopBeforeWaiting,kCFRunLoopExit kCFRunLoopBeforeWaiting时调用_objc_autoreleasePoolPop()_objc_autoreleasePoolPush()释放旧池,创建新池,kCFRunLoopExit时调用_objc_autoreleasePoolPop()

4.解析info.plist文件

5.调用application:didFinishLaunchingWithOptions:

首屏渲染: info.plist文件中 如果 有值,加载SXMain.storyboard

创建一个UIWindow实例显示界面

设置窗口根控制器

根据storyboard设置,创建控制器,设置此为window的根控制器

设置self.window可见并设置UIApplication的keyWindow

将根控制器的view添加到window上

没有值,需要代码初始化

    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; //创建window对象
    UIViewController *viewController = [[UIViewController alloc] init];   
    self.window.rootViewController = viewController;    //设置根控制器
   [self.window makeKeyAndVisible];   //将window设置为UIApplication的keyWindow,让window可见

三.首屏渲染完成后

其他业务模块初始化,监听注册,配置读取

阶段为首屏渲染完成到didFinishLaunchingWithOptions方法结束

www.jishuwen.com/d/2xl9

attribute(blog.sunnyxx.com/2016/05/14/…) (www.jianshu.com/p/0237c3415…