美文网首页
笔记-iOS应用程序的启动过程

笔记-iOS应用程序的启动过程

作者: 佐_笾 | 来源:发表于2019-03-29 19:55 被阅读0次

    程序的启动

    1. 使用Xcode打开一个项目,很容易会发现一个文件main.m文件,此处就是应用的入口。
    2. 程序启动时,先执行main函数,main函数是iOS程序的入口点
    3. 内部会调用UIApplicationMain函数
    4. UIApplicationMain里会创建一个UIApplication对象
    5. 然后创建UIAPPlication的delegate对象
    6. 然后AppDelegate,开启一个消息循环(main runloop)每当监听到对应的系统事件时,就会通知AppDelegate
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    

    UIApplication对象是应用程序的象征,每一个应用都有自己的UIApplication对象,而且是单例的。通过[UIApplication sharedApplication]可以获得这个单例对象,一个iOS程序启动后创建的第一个对象就是UIApplication对象,利用UIApplication对象,能进行一些应用级别的操作。

    UIApplicationMain函数实现如下

    int UIApplicationMain {
        int argc,
        char *argv[],
        NSString *principalClassName,
        NSString *delegateClassName
    }
    

    第一个参数表示程序在进入mian函数是的参数的个数,默认为1
    第二个参数表示装载函数的数组(包含的各个参数),默认为程序的名字
    第三个参数是UIApplication类名或其子类名,若是nil,则默认使用UIApplication类名。
    第四个参数是协议UIApplicationDelegate的实例化对象名,这个对象就是UIApplication对象监听到系统变化的时候通知其执行的相应方法。

    在UIApplicationMain函数中,根据传入的UIApplication名称和它的代理的名称,会主要做下面的事情:

    • 根据传入的名称创建UIApplication对象
    • 根据传入的代理名称创建UIApplication代理对象
    • 开启事件循环(如果不进行循环,那么在main函数结束后程序就结束了。要保证程序创建后可以一直存在)
    • 解析Info.plist文件:
      会在Info.plist文件里查找Main storyboard file base name这个Key对应的Value是否有值。如果有值,则表示之后回通过Storyboard加载控制器,APPDelegate会接收到didFinishLaunchingWithOptions消息(程序启动完成的时候),此时storyboard会进行一系列的加载操作;如果没有值,则不会通过storyboard加载控制器,接着AppDelegate会接收到didFinishLaunchingWithOptions消息(程序启动完成的时候),这个时候需要我们通过代码的方式加载控制器。

    启动完毕会调用didFinishLaunching方法,并在这个方法中创建UIWindow,设置AppDelegate的window属性,并设置UIWindow的根控制器

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        UIViewController *viewController = [[UIViewController alloc] init];
        self.window.rootViewController = viewController;
        // 此时根控制器的view还没有加到self.window上
        [self.window makeKeyAndVisible];
        // 此时根控制器的view加到self.window上
        return YES;
    }
    

    首先创建窗口,得到一个正确的UIWindow实例对象用来显示界面(self.window是系统自带的属性)。接着设置窗口的根控制器。自己创建控制器,设置这个控制器为self.window的根控制器。注意这个时候根控制器的view还没有加到self.window上,当窗口要显示的时候,才会把窗口的根控制器的view添加到窗口。
    显示窗口:

    [self.window makeKeyAndVisible] // 实际做了下面的事
    

    首先将self.window设置为UIApplication的keyWindow,这么做是方便我们以后查看UIApplication的主窗口是哪一个。
    接着,让self.window可见,相当于执行的代码是:

    self.window.hidden = NO;
    

    那么既然makeKeyAndVisible执行的是以上操作,实际上将[self.window makeKeyAndVisible]替换为self.window.hidden = NO,那么界面也会正常显示出来,因为makeKeyAndVisible内部就是这么做的。但是此时并没有设置UIApplication的keyWindow,为了以后方便访问,还是用makeKeyAndVisible更好一点。

    UIWindow的补充

    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以及状态栏就可以按层级关系正常显示。

    概览

    1. 先执行main函数,main内部会调用UIApplicationMain函数
    2. UIApplicationMain函数里面做了什么事情
      • 创建UIApplication对象
      • 创建UIApplication的delegate对象——AppDelegate
      • 开启一个消息循环:每监听到对应的系统事件时,就会通知AppDelegate
      • 为应用程序创建一个UIWindow对象(继承自UIView),设置为AppDelegate的window属性
      • 加载Info.plist文件,读取最主要storyboard文件的名称
      • 加载最主要的storyboard文件,创建白色箭头所指的控制器对象
      • 并且设置上一步创建的控制器为UIWindow的rootViewController属性(根控制器)
      • 展示UIWindow,展示之前会将添加rootViewController的view到UIWindow上面(在这一步才回创建控制器的view)
    [window addSubview:window.rootViewController.view];
    

    相关文章

      网友评论

          本文标题:笔记-iOS应用程序的启动过程

          本文链接:https://www.haomeiwen.com/subject/iebrbqtx.html