前言
文章中的觀點主要通過閱讀蘋果官方文檔和代碼調(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方法,這個方法承載了app啟動過程中的三個主要工作:
根據(jù)方法的第三個參數(shù)挨摸,創(chuàng)建UIApplication對象孩革。如果參數(shù)為nil, 則使用UIApplication類。這個UIApplication對象會以單例的方式存在于app的整個生命周期得运,直到app退出;
根據(jù)第四個參數(shù),創(chuàng)建UIApplication Delegate對象;
創(chuàng)建主事件循環(huán)(RunLoop)并啟動膝蜈。
加載info.plist, 如plist文件中配置了StoryBoard, 則加載Storyboard中的view.
雖然UIApplicationMain有返回值,但是在整個程序運行期間不會返回熔掺,只有在app退出時才會返回饱搏。
實踐才是檢驗真理的唯一標準!
驗證一: UIApplication對象和RunLoop對象的創(chuàng)建
創(chuàng)建一個新的工程置逻,并打開main.m 文件推沸,在main方法體內(nèi)打上斷點
,同時在AppDelegate.m的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法體內(nèi)也打上斷點,重新運行一下項目鬓催。
項目會停留在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)建。
驗證二:Storyboard的加載
Xcode會在新建的工程中自動創(chuàng)建一個Main.storyboard文件课舍,并在info.plist配置好塌西。保留驗證一里設(shè)置的AppDelegate中設(shè)置的斷點,重新運行程序布卡,在斷點出打印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這一項忿等,重新運行項目,在didFinishLaunchingWithOptions方法中打印self.window
po self.window // nil
驗證三:無論是否有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的情況贸街,通過驗證可以得出如果有storyboard, UIApplicationMain會先加載storyboard,如果沒有,則需要使用代碼在didFinishLaunchingWithOptions回調(diào)方法里創(chuàng)建self.window對象狸相。
最后梳理一下app啟動過程:
調(diào)用main()方法薛匪;
-
調(diào)用并進入UIApplicationMain()方法, 直到程序退出時方法才會退出。其內(nèi)部的執(zhí)行順序為:
a. 創(chuàng)建UIApplication對象脓鹃;
b. 創(chuàng)建UIApllication的delegate對象逸尖;
c. 加載info.plist文件,如果配置有storyboard文件名瘸右,則加載 storyboard;
d. 開啟一個主線程的RunLoop,監(jiān)聽事件娇跟。
現(xiàn)在,可以回頭看下蘋果的流程圖太颤。