前言
文章中的觀點(diǎn)主要通過閱讀蘋果官方文檔和代碼調(diào)試結(jié)果得出,如有偏差或者遺漏的地方,歡迎留言指出。
這張圖來自于蘋果的官方文檔,大致描述了app的啟動流程,可以先跳過不看。
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
在Xcode中打開main.m文件,可以看到在main方法中調(diào)用了UIApplicationMain方法,這個(gè)方法承載了app啟動過程中的三個(gè)主要工作:
根據(jù)方法的第三個(gè)參數(shù),創(chuàng)建UIApplication對象。如果參數(shù)為nil, 則使用UIApplication類。這個(gè)UIApplication對象會以單例的方式存在于app的整個(gè)生命周期,直到app退出;
根據(jù)第四個(gè)參數(shù),創(chuàng)建UIApplication Delegate對象;
創(chuàng)建主事件循環(huán)(RunLoop)并啟動。
加載info.plist, 如plist文件中配置了StoryBoard, 則加載Storyboard中的view.
雖然UIApplicationMain有返回值,但是在整個(gè)程序運(yùn)行期間不會返回,只有在app退出時(shí)才會返回。
實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)!
驗(yàn)證一: UIApplication對象和RunLoop對象的創(chuàng)建
創(chuàng)建一個(gè)新的工程,并打開main.m 文件,在main方法體內(nèi)打上斷點(diǎn)
,同時(shí)在AppDelegate.m的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法體內(nèi)也打上斷點(diǎn),重新運(yùn)行一下項(xiàng)目。
項(xiàng)目會停留在main方法中,在debug窗口中打印UIApllication對象和RunLoop對象
po [UIApplication sharedApplication] //返回nil
po [NSRunLoop currentRunLoop].currentMode //返回nil
繼續(xù)執(zhí)行程序,程序會停留在didFinishLaunchingWithOptions方法體內(nèi),在debug窗口中再次打印UIApplication對象和RunLoop對象
po [UIApplication sharedApplication] //返回<UIApplication: 0x7fa6a8400790>
po [NSRunLoop currentRunLoop].currentMode //返回UIInitializationRunLoopMode
由此可得出,UIApplication對象和RunLoop事件循環(huán)在UIApplicationMain方法中被創(chuàng)建。
驗(yàn)證二:Storyboard的加載
Xcode會在新建的工程中自動創(chuàng)建一個(gè)Main.storyboard文件,并在info.plist配置好。保留驗(yàn)證一里設(shè)置的AppDelegate中設(shè)置的斷點(diǎn),重新運(yùn)行程序,在斷點(diǎn)出打印self.window.rootViewController屬性
po self.window.rootViewController //<ViewController: 0x7f986ac08090>
由此可見,Main.storyboard在didFinishLaunchingWithOptions回調(diào)方法之前已經(jīng)被自動加載并設(shè)置為window的根對象。
刪除info.plist中的Main storyboard file base name這一項(xiàng),重新運(yùn)行項(xiàng)目,在didFinishLaunchingWithOptions方法中打印self.window
po self.window // nil
驗(yàn)證三:無論是否有storyboard,didFinishLaunchingWithOptions中回調(diào)中都可以設(shè)置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的情況,通過驗(yàn)證可以得出如果有storyboard, UIApplicationMain會先加載storyboard,如果沒有,則需要使用代碼在didFinishLaunchingWithOptions回調(diào)方法里創(chuàng)建self.window對象。
最后梳理一下app啟動過程:
調(diào)用main()方法;
-
調(diào)用并進(jìn)入U(xiǎn)IApplicationMain()方法, 直到程序退出時(shí)方法才會退出。其內(nèi)部的執(zhí)行順序?yàn)椋?/p>
a. 創(chuàng)建UIApplication對象;
b. 創(chuàng)建UIApllication的delegate對象;
c. 加載info.plist文件,如果配置有storyboard文件名,則加載 storyboard;
d. 開啟一個(gè)主線程的RunLoop,監(jiān)聽事件。
現(xiàn)在,可以回頭看下蘋果的流程圖。