详解iOS应用程序的启动过程
关键步骤
一个程序从main函数开始启动。
intmain(intargc,char*argv[]){ @autoreleasepool{ returnUIApplicationMain(argc,argv,nil,NSStringFromClass([AppDelegateclass])); } }
可以看到main函数会调用UIApplicationMain函数,它的四个参数的意思是:
- argc:代表程序在进入main函数时的参数的个数。默认为1。
- argv:代表包含的各个参数。默认为程序的名字。
- principalClassName:UIApplication或者它的子类的名字,如果传入的是nil,则表示UIApplication的名字,即@"UIApplication"。
- delegateClassName:UIApplication的代理的名字。
在UIApplicationMain函数中,根据传入的UIApplication名称和它的代理的名称,会主要做下面的事情:
- 根据传入的名称创建UIApplication对象。
- 根据传入的代理名称创建UIApplication代理对象。
- 开启事件循环(如果不进行循环,那么在main函数结束后程序就结束了。要保证程序创建后可以一直存在)。
- 解析Info.plist文件:
会在Info.plist文件里查找Mainstoryboardfilebasename这个Key对应的Value是否有值。如果有值,则表示之后会通过Storyboard加载控制器,AppDelegate会接收到didFinishLaunchingWithOptions消息(程序启动完成的时候),此时Storyboard会进行一系列的加载操作(后面会具体说);如果没有值,则不会通过Storyboard加载控制器,接着AppDelegate会接收到didFinishLaunchingWithOptions消息(程序启动完成的时候),在这个时候需要我们通过代码的方式加载控制器。
注意Info.plist中Mainstoryboardfilebasename这个Key并不是真正的Key,而是苹果为了增强可读性才这样写的,真正的Key为UIMainStoryboardFile(可以通过Info.plist文件的源代码查看)。
这就是在想要用代码方式创建控制器而不是Storyboard创建控制器的时候为什么先要将MainInterface设置为空白,这样在解析Info.plist文件的时候才会知道不通过Storyboard创建控制器。
由此可以知道,解析Info.plist文件这一操作主要是看我们用的是Storyboard方式加载还是代码的方式加载。默认Mainstoryboardfilebasename为Main,也就是通过Storyboard方式加载控制器。
现在具体分析一下,通过Storyboard方式加载控制器和代码方式加载控制器。
通过Storyboard
通过Storyboard,主要做了下面的事情(这些事情不需要我们做,是系统自动完成的,在程序启动完成的时候):
创建窗口。
创建一个UIWindow的实例用来显示界面。
设置窗口的根控制器。
根据Storyboard的设置,创建一个控制器。
并且设置这个控制器为之前创建的window的根控制器。
显示窗口。(相当于后面提到的makeKeyAndVisible)
设置self.window可见并且设置UIApplication的keyWindow。
在这一步中将根控制器的view添加到window上。
通过代码方式
通过代码的方式,需要我们在didFinishLaunchingWithOptions方法中进行加载控制器的相关操作。
-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions{
self.window=[[UIWindowalloc]initWithFrame:[UIScreenmainScreen].bounds]; UIViewController*viewController=[[UIViewControlleralloc]init]; self.window.rootViewController=viewController; //此时根控制器的view还没有加到self.window上 [self.windowmakeKeyAndVisible]; //此时根控制器的view加到self.window上 returnYES; }