基础篇-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的接收)
  • 注册通知、推送等
  • 内存警告
  # 当应用程序启动完毕的时候就会调用(系统自动调用)
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

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

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

  # 应用程序进入后台的时候调用
  # 一般在该方法中保存应用程序的数据, 以及状态
 - (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,然后使窗口显在示屏幕上。

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏進无尽的文章

基础篇-应用之间的跳转

在应用A跳转到应用B,则给A、B自身自定义URL Schemes(自定义的协议头)后,通过在A中处理B的URL Schemes,就可以在A中启动B了。

17210
来自专栏iOS开发攻城狮的集散地

Url Scheme实现APP间通信、分享

22350
来自专栏iOS开发随笔

Unity项目嵌入现有iOS项目的方法

32470
来自专栏娱乐心理测试

iOS 如何把图片资源打包成bundle文件及遇到的坑(详解)

(1.)"Base SDK" 设置为 "Latest iOS (iOS 11.2)" (Xcode 9.2为例)

35820
来自专栏xx_Cc的学习总结专栏

iOS-控制器View的创建和生命周期

35860
来自专栏iOS开发攻城狮的集散地

Url Scheme实现APP间通信、分享

接下来就以我之前写的UIActivityViewController系统原生分享-仿简书分享和iOS开源小项目-WSL两个Demo为例,让我们看下怎么可以让UI...

27080
来自专栏非典型技术宅

iOS四大对象之AppDelegate及UIApplicationMain函数/程序启动过程1. AppDelegate应用程序代理2. UIApplicationMain函数/程序启动过程

15850
来自专栏ShaoYL

程序启动的完整过程

465150
来自专栏哈雷彗星撞地球

(译)openURL 在 iOS10中已弃用

翻译自:openURL Deprecated in iOS10 译者:Haley_Wong

12710
来自专栏一“技”之长

iOS网络编程之四——请求类NSURLRequest使用详解

        在前面几篇博客中,介绍了iOS开发中的网络编程相关内容并且介绍了常用了两个平行的网络框架NSURLSession和NSURLConnection...

9420

扫码关注云+社区

领取腾讯云代金券