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