OC文件在編譯后赡鲜,類相關(guān)的數(shù)據(jù)結(jié)構(gòu)會(huì)保留在目標(biāo)文件中,在運(yùn)行時(shí)得到解析和使用氮帐。在應(yīng)用程序運(yùn)行起來(lái)的時(shí)候隘截,類的信息會(huì)有加載和初始化過(guò)程扎阶,這個(gè)過(guò)程就涉及到了類的兩個(gè)類方法:load
和initialize
。下面我們就來(lái)介紹一下這2個(gè)方法的區(qū)別婶芭。
1.load方法
1.1 調(diào)用時(shí)機(jī)
啟動(dòng)程序時(shí)东臀,參與編譯的類、分類會(huì)被加載進(jìn)內(nèi)存雕擂,load
方法就是在類被加載的時(shí)候調(diào)用的(前提是這個(gè)類有實(shí)現(xiàn)load
方法)啡邑。
該方法的調(diào)用和這個(gè)類是否被使用無(wú)關(guān)贱勃,即使一個(gè)類在整個(gè)程序中都沒(méi)有用到井赌,也沒(méi)有任何一個(gè)文件去引用該類的頭文件谤逼,該類的load
方法一樣會(huì)被調(diào)用。
等所有類仇穗、分類都加載進(jìn)內(nèi)存后才會(huì)調(diào)用程序的main函數(shù)流部,所以所有類的load
方法都是在main
函數(shù)之前被調(diào)用的。而且每個(gè)類纹坐、分類的load
方法都只會(huì)被調(diào)用一次
1.2 調(diào)用順序
一個(gè)城西中如果所有的類枝冀、分類都實(shí)現(xiàn)了load
方法,那么所有的load
方法都會(huì)被調(diào)用耘子。他們的執(zhí)行順序遵循以下規(guī)則:
- 先執(zhí)行類的
load
方法果漾,再執(zhí)行所有分類的load
方法 - 執(zhí)行類的
load
方法時(shí),是按照參與編譯的順序執(zhí)行谷誓,先編譯的類先執(zhí)行绒障。 - 先執(zhí)行父類的
load
方法,再執(zhí)行自己的load
方法 - 執(zhí)行分類的的
load
方法時(shí)捍歪,按照分類參與編譯的順序户辱,先參與編譯的分類先執(zhí)行
1.3 執(zhí)行方式
當(dāng)分類中存在和本類中同名的方法時(shí),調(diào)用這個(gè)方法最終執(zhí)行的是分類中的方法糙臼。按理說(shuō)調(diào)用load
方法時(shí)最終只會(huì)調(diào)用其中一個(gè)分類的load
方法庐镐,可是本類和分類都調(diào)用了load
方法
因?yàn)?code>load方法和普通方法調(diào)用方式不同,普通方法調(diào)用時(shí)通過(guò)消息發(fā)送機(jī)制實(shí)現(xiàn)的变逃,會(huì)先去類或元類的方法列表中查找必逆,如果找到了方法就執(zhí)行,如果沒(méi)有找到就去父類的方法列表中查找揽乱,只要找到就會(huì)終止查找末患,所以只會(huì)執(zhí)行一次。
而load
方法調(diào)用時(shí)锤窑,每個(gè)類都是load
方法的地址直接調(diào)用璧针,而不會(huì)走objc_msgSend
函數(shù)的方法查找流程,也就是說(shuō)一個(gè)類有時(shí)限load
方法就執(zhí)行渊啰,沒(méi)有就不執(zhí)行(沒(méi)有的話也不會(huì)去父類里面查找)
1.4 實(shí)現(xiàn)load方法時(shí)的注意點(diǎn)
我們通常在load
方法中進(jìn)行方法交換(Method Swizzle)探橱,除此之外,除非真的有必要绘证,我們盡量不要在load
方法中寫(xiě)代碼隧膏,尤其不要在load
方法中使用其它的類,因?yàn)檫@個(gè)時(shí)候其它的類可能還沒(méi)有被加載進(jìn)內(nèi)存嚷那,隨意使用可能會(huì)出問(wèn)題胞枕。
如果確實(shí)要在load
方法寫(xiě)一些代碼,那也要盡量精簡(jiǎn)代碼魏宽,不要做一些耗時(shí)或者等待鎖的操作腐泻,因?yàn)檎麄€(gè)程序在執(zhí)行load
方法時(shí)都會(huì)阻塞决乎,從而導(dǎo)致程序啟動(dòng)時(shí)間過(guò)長(zhǎng)甚至無(wú)法啟動(dòng)。
2. initialize 方法
2.1 調(diào)用時(shí)機(jī)
initialize
方法是在類或者子類收到第一條消息時(shí)被調(diào)用派桩,這里的消息就是指實(shí)例方法或者類方法的調(diào)用构诚,所以所有類的initialize
調(diào)用是在main函數(shù)后調(diào)用的。而且一個(gè)類只會(huì)調(diào)用一次initialize
方法铆惑。如果一個(gè)類在城西運(yùn)行過(guò)程中一直沒(méi)有被使用過(guò)范嘱,那這個(gè)類的initialize
方法也就不會(huì)被調(diào)用
2.2 調(diào)用方式
initialize
方法的調(diào)用和普通方法調(diào)用一樣,也是走的objc_msgSend流程员魏。所以如果一個(gè)類和他的分類都實(shí)現(xiàn)了initialize
方法丑蛤,那么最終調(diào)用的是分類中的方法
如果子類和父類都實(shí)現(xiàn)了initialize
方法,那么會(huì)先調(diào)用父類的方法撕阎,然后調(diào)用子類的方法盏阶。
子類中不需要寫(xiě)[super initialize]來(lái)調(diào)用父類的方法,通過(guò)查看源碼得知它是在底層實(shí)現(xiàn)過(guò)程中主動(dòng)調(diào)用的父類的initialize
方法
2.3 實(shí)現(xiàn)initialize方法時(shí)的注意點(diǎn)
雖然使用initialize
要比使用load安全(因?yàn)樵谡{(diào)用initialize
時(shí)所有類已經(jīng)被加載進(jìn)內(nèi)存了)闻书,但我們還是要盡量少用initialize
這個(gè)方法個(gè)名斟,尤其要謹(jǐn)慎在分類中實(shí)現(xiàn)initialize
方法,因?yàn)槿绻诜诸愔袑?shí)現(xiàn)了魄眉,本類實(shí)現(xiàn)的initialize
方法將不會(huì)被調(diào)用砰盐。實(shí)際開(kāi)發(fā)中initialize
方法一般用于初始化全局變量或靜態(tài)變量。
3 總結(jié)
- 調(diào)用時(shí)機(jī)和調(diào)用順序不同:
load方法是在類被加載進(jìn)內(nèi)存時(shí)調(diào)用的坑律,在main函數(shù)調(diào)用前被調(diào)用岩梳,且父類,子類晃择,分類都會(huì)被調(diào)用冀值,父類>子類>分類,同一級(jí)別的按編譯順序調(diào)用
initialize方法是在第一次方法調(diào)用時(shí)被調(diào)用宫屠,在main函數(shù)后
- 執(zhí)行方式不同:
load方法調(diào)用時(shí)列疗,每個(gè)類都是根據(jù)load方法的地址直接調(diào)用,不會(huì)走objc_msgSend流程浪蹂,也就是說(shuō)一個(gè)類有實(shí)現(xiàn)load方法就執(zhí)行抵栈,沒(méi)有就不執(zhí)行,沒(méi)有的話也不會(huì)再父類里面查找
initialize方法調(diào)用方式和objc_msgSend流程一致坤次,initialize默認(rèn)會(huì)調(diào)用
[super initialize]
- 使用模式不同:
load方法常用于方法交換
Method_Swizzle
initialize常用于初始化全局變量或靜態(tài)變量