前言
文章中的观点主要通过阅读苹果官方文档和代码调试结果得出,如有偏差或者遗漏的地方,欢迎留言指出。
这张图来自于苹果的官方文档,大致描述了app的启动流程,可以先跳过不看。
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
在Xcode中打开main.m文件,可以看到在main方法中调用了UIApplicationMain方法,这个方法承载了app启动过程中的三个主要工作:
根据方法的第三个参数,创建UIApplication对象。如果参数为nil, 则使用UIApplication类。这个UIApplication对象会以单例的方式存在于app的整个生命周期,直到app退出;
根据第四个参数,创建UIApplication Delegate对象;
创建主事件循环(RunLoop)并启动。
加载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启动过程:
调用main()方法;
-
调用并进入UIApplicationMain()方法, 直到程序退出时方法才会退出。其内部的执行顺序为:
a. 创建UIApplication对象;
b. 创建UIApllication的delegate对象;
c. 加载info.plist文件,如果配置有storyboard文件名,则加载 storyboard;
d. 开启一个主线程的RunLoop,监听事件。
现在,可以回头看下苹果的流程图。