(一)应用程序的运行状态
任何iOS应用都有以下五种运行状态:
(1)Not Running:程序为启动
(2)Inactive:程序在前台,但是没有用户事件的处理;
(3)Active:程序在前台,当前正在处理用户事件(常态);
(4)Background:程序在后台运行,但可以在后台运行;
(5)Suspend:挂起,程序在后台但没有运行,内存告急时被清理。
https://blog.csdn.net/totogo2010/article/details/8048652/
(二)应用程序的启动流程
万物始于Main函数 ---> UIApplicationMain函数 ,在该函数中,做三件事:
(1)创建UIApplication实例:
声明UIApplication类、UIApplicationDelegate协议/指针(内部设置代理,暗调协议方法);
(2)创建AppDelegate实例:
遵守协议,实现协议;创建展示窗口Window;
(3)开启NSRunLoop消息循环:
保持随时处理系统事件的循环状态;
当UIApplication实例接收到系统事件时,它通知AppDelegate实例去处理,在AppDelegate实例内部:
(1)创建展示窗口window,并赋值给自己的属性window;
(2)【解析plist文件,加载最主要的storyboard(Main.storyboard),创建➡️指向的视图控制器对象,并赋值给window的根视图控制器;】【待定】
(3)在展示主window前,创建并添加到window上;
(4)展示window,并将控制器的view添加到window上。
⚠️:在解析plist文件时:会在Info.plist文件里查找Main storyboard file base name这个Key对应的Value是否有值。如果有值,则表示之后会通过Storyboard加载控制器。之后当AppDelegate接收到didFinishLaunchingWithOptions消息(程序启动完成的时候),Storyboard会进行一系列的加载操作(后面会具体说);如果没有值,则表示不会通过Storyboard加载控制器,接着AppDelegate会接收到didFinishLaunchingWithOptions消息,在该方法中需要我们通过代码的方式加载控制器。这就是在想要用代码方式创建控制器而不是Storyboard创建控制器的时候为什么先要将Main Interface设置为空白,这样在解析Info.plist文件的时候才会知道不通过Storyboard创建控制器。
⚠️:由此可以知道,解析Info.plist文件这一操作主要是看我们用的是Storyboard方式加载还是代码的方式加载。默认Main storyboard file base name为Main,也就是通过Storyboard方式加载控制器。
⚠️:[self.window makeKeyAndVisible];实际上做了一下三件事:
(a)将当前的窗口设置为应用的主窗口(keywindow);
(b)self.window.hidden = NO;使当前窗口可见(默认为YES,即隐藏);
(c)将根控制器的view会加到self.window上以备显示;
⚠️:AppDelegate创建的window属性的修饰符为什么是strong?
之所以使用weak来修饰一个控件是因为这个控件会被加到一个view中,这个view的subViews数组会有强引用指向控件,所以用weak是没有问题的。现在用strong,是因为window控件根本不会被加到其他view中!即没有其他的强指针指向这个对象,所以在创建的时候需要将修饰符设置成strong以保证创建出的window不会被销毁。
⚠️:window是有层级的,并且可以有多个window同时存在。
比如:状态栏就是一个window,键盘也是一个window。
可以通过设置UIWindow的对象的windowLevel属性来调整层级。
self.window.windowLevel = UIWindowLevelStatusBar;
window共有三种等级:UIWindowLevelNormal,UIWindowLevelStatusBar UIWindowLevelAlert。如果三种等级同时出现在屏幕上,那么alert在最上面,statusBar在中间,normal则在最下面。
注意:如果一个程序中有多个window,控制器默认会把状态栏隐藏。
解决办法:关闭控制器对状态栏的控制,(为Info.plist增加View controller-based status bar appearance这个key并设置为NO)这样这些window以及状态栏就可以按层级关系正常显示。