基础篇-UIApplication、UIWindow以及程序的启动流程看我就够了

前言

这篇文章主要介绍下有关UIApplication、UIWindow以及程序的启动流程,通过这篇文章,相信你会更加理解iOS 的应用启动过程以及app应用级别的相关知识。

UIApplication

简单介绍

UIApplication的一个主要工作是处理用户事件,它会起一个队列,把所有用户事件都放入队列,逐个处理,在处理的时候,它会发送当前事件到一个合适的处理事件的目标控件。此外,UIApplication实例还维护一个在本应用中打开的window列表(UIWindow实例),这样它就可以接触应用中的任何一个UIView对象。UIApplication实例会被赋予一个代理对象UIApplicationDelegate,以处理应用程序的生命周期事件(比如程序启动和关闭)、系统事件(比如来电、记事项警告)等等。

1.UIApplication对象是应用程序的象征,一个UIApplication对象就代表一个应用程序。
2.每一个应用都有自己的UIApplication对象,而且是单例的,如果试图在程序中新建一个UIApplication对象,那么将报错提示。
3.通过[UIApplicationsharedApplication]可以获得这个单例对象.
4. 一个iOS程序启动后创建的第一个对象就是UIApplication对象,且只有一个(通过代码获取两个UIApplication对象,打印地址可以看出地址是相同的)。
5.利用UIApplication对象,能进行一些应用级别的操作.

应用级别的操作示例

  • 设置应用程序图标右上角的红色提醒数字(@property(nonatomic) NSInteger applicationIconBadgeNumber)
  • 设置联网指示器的可见性(isNetworkActivityIndicatorVisible)
  • iOS 中的状态栏设置(iOS9 中跟状态栏有关的已经过期,状态栏交给控制器UIViewController管理了)
  • 应用界别的跳转 (openURL)

一 、设置应用程序图标右上角的红色提醒数字(如QQ消息的时候,图标上面会显示1,2,3条新信息等。)

@property(nonatomic) NSInteger applicationIconBadgeNumber;

UIApplication *app=[UIApplication sharedApplication];
app.applicationIconBadgeNumber=123;

二、设置联网指示器的可见性

@property(nonatomic,getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;

代码和效果: 
UIApplication *app=[UIApplication sharedApplication];
//设置指示器的联网动画
app.networkActivityIndicatorVisible=YES;

三、管理状态栏

通过UIApplication管理(一个应用程序的状态栏都由它统一管理)

 如果想利用UIApplication来管理状态栏,首先得修改Info.plist的设置

 View controller-based status bar appearance :NO
 Status bar is initially hidden  :NO
 Status bar style :Opaque black style

这样在Info.plist设置后状态栏是白色的,后续可以在单个VC中通过 UIApplication 随意修改状态栏状态。

四、应用界别的跳转 (openURL)
通过这个方法可以打开本机其他应用和远程连接。

URL补充:
URL:统一资源定位符,用来唯一的表示一个资源。
URL格式:协议头://主机地址/资源路径
网络资源:http/ ftp等   表示百度上一张图片的地址   http://www.baidu.com/images/20140603/abc.png
本地资源:file:///users/apple/desktop/abc.png(主机地址省略)

UIApplication Delegate

在app受到干扰时,会产生一些系统事件,这时UIApplication会通知它的delegate对象,让delegate代理来处理这些系统事件。当应用程序启动完毕的时候就会调用(系统自动调用)。

  • 应用程序的生命周期事件(启动,关闭,进入后台)
  • 应用级跳转(openURL的接收)
  • 注册通知、推送等
  • 内存警告
###########################  值得注意的地方是 ######################
  【1】在程序第一次启动的时候 不会调用applicationWillEnterForeground这个方法而
  会调用 didFinishLaunchingWithOptions 这个方法。
  【2】而当应用已经加载到内存后,从桌面图标点击进入应用时,就会调用applicationWillEnterForeground方法而
  不会调用 didFinishLaunchingWithOptions方法
##################################################################

 #当应用程序启动完毕的时候就会调用(系统自动调用)
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

 # 即将失去活动状态的时候调用(失去焦点, 不可交互)
 - (void)applicationWillResignActive:(UIApplication *)application

 # 重新获取焦点(能够和用户交互)
 - (void)applicationDidBecomeActive:(UIApplication *)application

  # 应用程序进入后台的时候调用
  # 一般在该方法中保存应用程序的数据, 以及状态
  # 双击Home键上滑杀掉APP也会调用这个方法【亲测】
 - (void)applicationDidEnterBackground:(UIApplication *)application

 # 应用程序即将进入前台的时候调用
 #一般在该方法中恢复应用程序的数据,以及状态
 - (void)applicationWillEnterForeground:(UIApplication *)application

  # 应用程序即将被销毁的时候会调用该方法
  #注意:如果应用程序处于挂起状态的时候无法调用该方法
 - (void)applicationWillTerminate:(UIApplication *)application

  #应用级跳转(openURL的接收)
  - (BOOL)application:(UIApplication *)application   openURL:(NSURL *)url   sourceApplication:(NSString *)sourceApplication    annotation:(id)annotation 
  // NOTE: 9.0以后使用新API接口
  - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options

  #注册通知、推送等
  - (void)application:(UIApplication *)application  didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 
  -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error 

  # 应用程序接收到内存警告的时候就会调用
  # 一般在该方法中释放掉不需要的内存
  - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application

UIWindow

  • UIWindow是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手动创建多个UIWindow,同时加到程序里面。
  • iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的View,最后将控制器的View添加到UIWindow上,于是控制器的View就显示在屏幕上了。
  • 一个iOS程序之所以能显示在屏幕上,完全是因为它有UIWindow,也就是说,没有UIWindow就看不到任何UI界面。
  • 状态栏和键盘都是特殊的UIWindow。

那么UIWindow是如何将View显示到屏幕上的呢?
这里有三个重要的对象UIScreen,UIWindow,UIView。

  • UIScreen对象识别物理屏幕连接到设备。
  • UIWindow对象提供绘画支持给屏幕。
  • UIView执行绘画,当窗口要显示内容的时候,UIView绘画出他们的内容并附加到窗口上。

这样View就显示在窗口上了

用代码来加载UIWindow

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  // 1.创建窗口
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];

  *************************** 类文件**********************************
  //创建窗口的根控制器,并且赋值
  UIViewController *rootVc = [[UIViewController alloc]init];
    self.window.rootViewController = rootVc;
  ************************  main.storyboard  ****************************
  // 2.加载main.storyboard,创建main.storyboard描述的控制器
  // UIStoryboard专门用来加载stroyboard
  // name:storyboard名称不需要后缀
  UIStoryboard *stroyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

  // 加载sotryboard描述的控制器
  // 加载箭头指向的控制器
    UIViewController *vc = [stroyboard instantiateInitialViewController];
  //根据绑定标识加载 
  //UIViewController *vc = [stroyboard instantiateViewControllerWithIdentifier:@"red"];

  // 设置窗口的根控制器
  self.window.rootViewController = vc;

  *****************************  xib ****************************
  // 创建窗口的根控制器
  // 通过xib创建控制器
  ViewController *vc = [[ViewController alloc] initWithNibName:@"VC" bundle:nil];
  self.window.rootViewController = vc;

  // 3.显示窗口
  [self.window makeKeyAndVisible];
  return YES;
}

KeyWindow

什么是keyWindow,官方文档中是这样解释的"The key window is the one that is designated to receive keyboard and other non-touch related events. Only one window at a time may be the key window." 翻译过来就是说,keyWindow是指定的用来接收键盘以及非触摸类的消息,而且程序中每一个时刻只能有一个window是keyWindow。

四个关于window变化的通知:

   UIWindowDidBecomeVisibleNotification
   UIWindowDidBecomeHiddenNotification
   UIWindowDidBecomeKeyNotification
   UIWindowDidResignKeyNotification

这四个通知对象中的object都代表当前已显示(隐藏),已变成keyWindow(非keyWindow)的window对象,其中的userInfo则是空的。于是我们可以注册这个四个消息,打印信息来观察keyWindow的变化以及window的显示,隐藏的变动。

UIWindow的层级

UIWindow是有层级的,层级高的显示在最外面,当层级相同时,越靠后调用的显示在外面。

UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal; //默认,值为0
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert; //值为2000 
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar ; // 值为1000

所以UIWindowLevelNormal < UIWindowLevelStatusBar< UIWindowLevelAlert
并且层级是可以做加减的self.window.windowLevel = UIWindowLevelAlert+1;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    self.window.backgroundColor = [UIColor yellowColor];
    [self.window makeKeyAndVisible];

    UIWindow *normalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   normalWindow.backgroundColor = [UIColor blueColor];
    normalWindow.windowLevel = UIWindowLevelNormal;
    [normalWindow makeKeyAndVisible];

    CGRect windowRect = CGRectMake(50, 50,  [[UIScreen mainScreen] bounds].size.width- 100,   [[UIScreen mainScreen] bounds].size.height - 100);
    UIWindow *alertLevelWindow = [[UIWindow alloc] initWithFrame:windowRect];
    alertLevelWindow.windowLevel = UIWindowLevelAlert;
    alertLevelWindow.backgroundColor = [UIColor redColor];
      [alertLevelWindow makeKeyAndVisible];

   UIWindow *statusLevelWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 50, 320, 20)];
    statusLevelWindow.windowLevel = UIWindowLevelStatusBar;
    statusLevelWindow.backgroundColor = [UIColor blackColor];
    [statusLevelWindow makeKeyAndVisible];

    NSLog(@"Normal window level: %f", UIWindowLevelNormal);
    NSLog(@"Normal window level: %f", UIWindowLevelAlert);
    NSLog(@"Normal window level: %f", UIWindowLevelStatusBar);

    return  YES;
}

通过运行结果我们可以注意到两点:

 1)我们生成的normalWindow虽然是在第一个默认的window之后调用makeKeyAndVisible,但是仍然没有显示出来。这说明当Level层级相同的时候,只有第一个设置为KeyWindow的显示出来,后面同级的再设置KeyWindow也不会显示。

2)statusLevelWindow在alertLevelWindow之后调用makeKeyAndVisible,淡仍然只是显示在alertLevelWindow的下方。这说明UIWindow在显示的时候是不管KeyWindow是谁,都是Level优先的,即Level最高的始终显示在最前面。


程序的启动过程

我们找到程序的入口main函数,来看程序的启动过程

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

这个默认的iOS程序就是从main函数开始执行的,但是在main函数中我们其实只能看到一个方法,这个方法内部是一个消息循环(相当于一个死循环),因此运行到这个方法UIApplicationMain之后程序不会自动退出,而只有当用户手动关闭程序这个循环才结束。此时我们可以根据UIApplicationMain函数了解程序启动的过程.

  1. 第一个参数和第二个参数其实就是main函数的参数,分别代表:参数个数、参数内容;
  2. 第三个参数代表UIApplication类(或子类)字符串,这个参数默认为nil则代表默认为UIApplication类,用户可以自定义一个类继承于这个类;如果为nil则等价于NSStringFromClass([UIApplication class]).
  3. 第四个参数是UIApplication的代理类字符串,默认生成的是AppDelegate类,这个类主要用于监听整个应用程序生命周期的各个事件,当UIApplication运行过程中引发了某个事件之后会调用代理中对应的方法;

程序启动的完整过程

  • 1. main函数

  • 2.UIApplicationMain
    根据第三个参数创建对应的UIApplication对象
    根据第四个参数AppDelegate创建并指定此对象为UIApplication的代理.
    开启主运行循环 main events loop处理事件,UIApplication会开启一个消息循环不断监听应用程序的各个活动,当应用程序生命周期发生改变UIApplication就会调用代理对应的方法。

  • 3.程序启动完毕的时候, 创建window,加载info.plist。
    (假如有storyboard)根据Info.plist中 Main storyboard file base name 字段获得最主要storyboard的文件名,加载对应的storyboard。
    系统在加载storyboard的时候会做以下三件事情
    1. 创建窗口 UIWindow。
    2. 加载mian.storyboard 并实例化view controller
    3. 分配新视图控制器到窗口root viewcontroller,然后使窗口显在示屏幕上。

    (假如没有storyboard)就不会加载storyboard,也就不会帮我们创建UIWindow,那么我们需要自己在程序启动完成的时候也就是在didFinishLaunchingWithOptions方法中创建。
    1.创建窗口 UIWindow。
    2. 创建并实例化view controller
    3. 分配新视图控制器到窗口root viewcontroller,然后使窗口显在示屏幕上。

四大对象关系图和程序启动流程图

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

推荐阅读更多精彩内容