iOS APP启动函数调用顺序~详解
//聯系人:石虎? QQ: 1224614774昵稱:嗡嘛呢叭咪哄
一、OC調用
C++ 會為靜態創建的對象生成初始化器,與靜態語言不同,OC基于Runtime機制可以用類的名字來實例化一個類的對象。Runtime 維護了一張映射類名與類的全局表,當加載一個 dylib 時,其定義的所有的類都需要被注冊到這個全局表中。ObjC 在加載時可以通過 fix-up 在動態類中改變實例變量的偏移量,利用這個技術可以在不改變dylib的情況下添加另一個 dylib 中類的方法,而非常見的通過定義類別(Category)的方式改變一個類的方法。
主執行文件和相關的 dylib的依賴關系構成了一張巨大的有向圖,執行初始化器先加載葉子節點,然后逐步向上加載中間節點,直至最后加載根節點。這種加載順序確保了安全性,加載某個 dylib 前,其所依賴的其余 dylib 文件肯定已經被預先加載。最后 dyld 會調用 main() 函數。main() 會調用 UIApplicationMain(),程序啟動。
二、程序啟動邏輯
使用Xcode打開一個項目,很容易會發現一個文件--main.m文件,此處就是應用的入口了。程序啟動時,先執行main函數,main函數是ios程序的入口點,內部會調用UIApplicationMain函數,UIApplicationMain里會創建一個UIApplication對象 ,然后創建UIApplication的delegate對象 —–(您的)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
???
}
第一個參數表示參數的個數,第二個參數表示裝載函數的數組,第三個參數,是UIApplication類名或其子類名,若是nil,則默認使用UIApplication類名。第四個參數是協議UIApplicationDelegate的實例化對象名,這個對象就是UIApplication對象監聽到系統變化的時候通知其執行的相應方法。
啟動完畢會調用 didFinishLaunching方法,并在這個方法中創建UIWindow,設置AppDelegate的window屬性,并設置UIWindow的根控制器。如果有storyboard,會根據info.plist中找到應用程序的入口storyboard并加載箭頭所指的控制器,顯示窗口。storyboard和xib最大的不同在于storyboard是基于試圖控制器的,而非視圖或窗口。展示之前會將添加rootViewController的view到UIWindow上面(在這一步才會創建控制器的view)
1 [window?addSubview:?window.rootViewControler.view];
每個應用程序至少有一個UIWindow,這window負責管理和協調應用程序的屏幕顯示,rootViewController的view將會作為UIWindow的首視圖。
未使用storyboard的啟動
四、程序啟動的完整過程如下:
1.main 函數
2.UIApplicationMain
- 創建UIApplication對象
- 創建UIApplication的delegate對象
- delegate對象開始處理(監聽)系統事件(沒有storyboard)
- 程序啟動完畢的時候, 就會調用代理的application:didFinishLaunchingWithOptions:方法
- 在application:didFinishLaunchingWithOptions:中創建UIWindow
- 創建和設置UIWindow的rootViewController
- 顯示窗口
3.根據Info.plist獲得最主要storyboard的文件名,加載最主要的storyboard(有storyboard)
- 創建UIWindow
- 創建和設置UIWindow的rootViewController
- 顯示窗口
五、AppDelegate的代理方法
//app啟動完畢后就會調用
-?(BOOL)application:(UIApplication?*)application?didFinishLaunchingWithOptions:(NSDictionary?*)launchOptions
{
}
?
//app程序失去焦點就會調用????????????????????
-?(void)applicationWillResignActive:(UIApplication?*)application
{
}
?
//app進入后臺的時候調用,?一般在這里保存應用的數據(游戲數據,比如暫停游戲)
-?(void)applicationDidEnterBackground:(UIApplication?*)application
{
}
?
//app程序程序從后臺回到前臺就會調用
-?(void)applicationWillEnterForeground:(UIApplication?*)application
{
}
?
//app程序獲取焦點就會調用
-?(void)applicationDidBecomeActive:(UIApplication?*)application
{
?}
?
//?內存警告,可能要終止程序,清除不需要再使用的內存
-?(void)applicationDidReceiveMemoryWarning:(UIApplication?*)application
{
}
?
//?程序即將退出調用
-?(void)applicationWillTerminate:(UIApplication?*)application
{
}
AppDelegate加載順序
1.application:didFinishLaunchingWithOptions:
2.applicationDidBecomeActive:
ViewController中的加載順序
1.loadView
2.viewDidLoad
3.viewWillAppear
4.viewWillLayoutSubviews
5.viewDidLayoutSubviews
6.viewDidAppear
View中的加載順序
1.initWithCoder(如果沒有storyboard就會調用initWithFrame,這里兩種方法視為一種)
2.awakeFromNib
3.layoutSubviews
4.drawRect
一些方法的使用時機
1 +?(void)load;
應用程序啟動就會調用的方法,在這個方法里寫的代碼最先調用。
1 +?(void)initialize;
用到本類時才調用,這個方法里一般設置導航控制器的主題等,如果在后面的方法設置導航欄主題就太遲了!
1 -?(BOOL)application:(UIApplication?*)application?didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
這個方法里面會創建UIWindow,設置根控制器并展現,比如某些應用程序要加載授權頁面也是在這加,也可以設置觀察者,監聽到通知切換根控制器等。
1 -?(void)awakeFromNib;
在使用IB的時候才會涉及到此方法的使用,當.nib文件被加載的時候,會發送一個awakeFromNib的消息到.nib文件中的每個對象,每個對象都可以定義自己的awakeFromNib函數來響應這個消息,執行一些必要的操作。在這個方法里設置view的背景等一系列普通操作。
1 -?(void)loadView;
創建視圖的層次結構,在沒有創建控制器的view的情況下不能直接寫 self.view 因為self.view的底層是:
if(_view?==?nil){
_view?=?[self?loadView]
}
這么寫會直接造成死循環。
如果重寫這個loadView方法里面什么都不寫,會顯示黑屏。
1 -?(void)viewWillLayoutSubviews;
視圖將要布局子視圖,蘋果建議的設置界面布局屬性的方法,這個方法和viewWillAppear里,系統的底層都是沒有寫任何代碼的,也就是說這里面不寫super 也是可以的。
1 -?(void)layoutSubviews;
在這個方法里一般設置子控件的frame。
1 -?(void)drawRect:(CGRect)rect;
UI控件都是畫上去的,在這一步就是把所有的東西畫上去。drawRect方法只能在加載時調用一次,如果后面還需要調用,比如下載進度的圓弧,需要一直刷幀,就要使用setNeedsDisplay來定時多次調用本方法。
1 -?(void)applicationDidBecomeActive:(UIApplication?*)application;
這是AppDelegate的應用程序獲取焦點方法,真正到了這里,才是所有東西全部加載完畢。
六、啟動分析
應用啟動時,會播放一個啟動動畫。iPhone上是400ms,iPad上是500ms。如果應用啟動過慢,用戶就會放棄使用,甚至永遠都不再回來。為了防止一個應用占用過多的系統資源,開發iOS的蘋果工程師門設計了一個“看門狗”的機制。在不同的場景下,“看門狗”會監測應用的性能。如果超出了該場景所規定的運行間,“看門狗”就會強制終結這個應用的進程。
iOS App啟動時會鏈接并加載Framework和static lib,執行UIKit初始化,然后進入應用程序回調,執行Core Animation transaction等。每個Framework都會增加啟動時間和占用的內存,不要鏈接不必要的Framework,必要的Framework不要標記為Optional。避免創建全局的C++對象。
初始化UIKit時字體、狀態欄、user defaults、Main.storyboard會被初始化。User defaults本質上是一個plist文件,保存的數據是同時被反序列化的,不要在user defaults里面保存圖片等大數據。
對于 OC 來說應盡量減少 Class,selector 和 category 這些元數據的數量。編碼原則和設計模式之類的理論會鼓勵大家多寫精致短小的類和方法,并將每部分方法獨立出一個類別,但這會增加啟動時間。在調用的地方使用初始化器,不要使用\\atribute((constructor)) 將方法顯式標記為初始化器,而是讓初始化方法調用時才執行。比如使用 dispatch_once(),pthread_once() 或 std::once()。也就是在第一次使用時才初始化,推遲了一部分工作耗時。
建立網絡連接前需要做域名解析,如果網關出現問題,dns解析不正常時,dns的超時時間是應用控制不了的。在程序設計時要考慮這些問題,如果程序啟動時有網絡連接,應盡快的結束啟動過程,網絡訪問通過線程解決,而不阻塞主線程的運行。
謝謝!!!
總結
以上是生活随笔為你收集整理的iOS APP启动函数调用顺序~详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最简单DIY基于ESP32CAM的物联网
- 下一篇: 安卓虚拟环境搭建