app启动过程分析

前言

文章中的观点主要通过阅读苹果官方文档和代码调试结果得出,如有偏差或者遗漏的地方,欢迎留言指出。

image

这张图来自于苹果的官方文档,大致描述了app的启动流程,可以先跳过不看。

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

在Xcode中打开main.m文件,可以看到在main方法中调用了UIApplicationMain方法,这个方法承载了app启动过程中的三个主要工作:

  1. 根据方法的第三个参数,创建UIApplication对象。如果参数为nil, 则使用UIApplication类。这个UIApplication对象会以单例的方式存在于app的整个生命周期,直到app退出;

  2. 根据第四个参数,创建UIApplication Delegate对象;

  3. 创建主事件循环(RunLoop)并启动

  4. 加载info.plist, 如plist文件中配置了StoryBoard, 则加载Storyboard中的view.

虽然UIApplicationMain有返回值,但是在整个程序运行期间不会返回,只有在app退出时才会返回。

实践才是检验真理的唯一标准!

验证一: UIApplication对象和RunLoop对象的创建

创建一个新的工程,并打开main.m 文件,在main方法体内打上断点
,同时在AppDelegate.m的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法体内也打上断点,重新运行一下项目。

项目会停留在main方法中,在debug窗口中打印UIApllication对象和RunLoop对象

po [UIApplication sharedApplication] //返回nil
po [NSRunLoop currentRunLoop].currentMode //返回nil

继续执行程序,程序会停留在didFinishLaunchingWithOptions方法体内,在debug窗口中再次打印UIApplication对象和RunLoop对象

po [UIApplication sharedApplication] //返回<UIApplication: 0x7fa6a8400790>
po [NSRunLoop currentRunLoop].currentMode   //返回UIInitializationRunLoopMode

由此可得出,UIApplication对象和RunLoop事件循环在UIApplicationMain方法中被创建。

验证二:Storyboard的加载

Xcode会在新建的工程中自动创建一个Main.storyboard文件,并在info.plist配置好。保留验证一里设置的AppDelegate中设置的断点,重新运行程序,在断点出打印self.window.rootViewController属性

 po self.window.rootViewController //<ViewController: 0x7f986ac08090>

由此可见,Main.storyboard在didFinishLaunchingWithOptions回调方法之前已经被自动加载并设置为window的根对象。

删除info.plist中的Main storyboard file base name这一项,重新运行项目,在didFinishLaunchingWithOptions方法中打印self.window

 po self.window // nil

验证三:无论是否有storyboard,didFinishLaunchingWithOptions中回调中都可以设置self.window的rootViewController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    self.window = [[UIWindow alloc] init];
    self.window.rootViewController = [[ViewController alloc] init];
    [self.window makeKeyAndVisible];
    return YES;
}

分别尝试有storyboard的情况和无storyboard的情况,通过验证可以得出如果有storyboard, UIApplicationMain会先加载storyboard,如果没有,则需要使用代码在didFinishLaunchingWithOptions回调方法里创建self.window对象。

最后梳理一下app启动过程:

  1. 调用main()方法;

  2. 调用并进入UIApplicationMain()方法, 直到程序退出时方法才会退出。其内部的执行顺序为:

    a. 创建UIApplication对象;

    b. 创建UIApllication的delegate对象;

    c. 加载info.plist文件,如果配置有storyboard文件名,则加载 storyboard;

    d. 开启一个主线程的RunLoop,监听事件。

现在,可以回头看下苹果的流程图。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 关键步骤 一个程序从main函数开始启动。代码如下: int main(int argc, char * argv...
    JzRo阅读 606评论 0 2
  • //联系人:石虎QQ: 1224614774昵称:嗡嘛呢叭咪哄 一、OC调用 C++会为静态创建的对象生成初始化器...
    石虎132阅读 2,673评论 0 19
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,216评论 30 472
  • 前言 随着前端项目的不断扩大,一个原本简单的网页应用所引用的js文件可能变得越来越庞大。尤其在近期流行的单页面应用...
    AlienZHOU阅读 17,213评论 11 26
  • 泥人 绵延的雨季已然来临 你要记住风的模样 你要记住雨的模样 你要记住你自己的模样 因为你是一个泥人 一个泥人最后...
    等一场烟雨迷阅读 172评论 1 3