1府树、load方法
load方法會(huì)在runtime加載類、分類時(shí)調(diào)用(在main函數(shù)開始之前),與這個(gè)類是否用到無關(guān)仇参。
類、分類中的load方法
從蘋果源碼網(wǎng)站下載下來runtime的objc4-723源碼婆殿。
obje-runtime-new.mm文件中有個(gè)load_images方法指明加載文件到內(nèi)存中诈乒,其中call_load_methods這句代碼便對(duì)應(yīng)load方法的處理:
- 這里可以看出是先調(diào)用所有類文件里的load方法,再調(diào)用所有Category文件里的load方法
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
//首先調(diào)用類的load方法
call_class_loads();
}
// 2. Call category +loads ONCE
//然后調(diào)用分類的load方法
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
再看call_class_loads方法里的邏輯:
- 這里可以看出是找到每個(gè)class中l(wèi)oad方法存放的內(nèi)存地址婆芦,然后通過內(nèi)存地址來調(diào)用load方法的
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
//循環(huán)處理所有的類文件里的load方法
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
//這個(gè)load_method是個(gè)指針類型怕磨,里面存放的是load方法的內(nèi)存地址
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
//此處直接通過load方法的內(nèi)存地址來調(diào)用load方法
(*load_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
再看call_category_loads方法里的邏輯:
- 同樣,調(diào)用Category中的load方法也是直接通過內(nèi)存地址的方法調(diào)用消约,此處便不再作注解
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
// Detach current loadable list.
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
// Compact detached list (order-preserving)
shift = 0;
for (i = 0; i < used; i++) {
if (cats[i].cat) {
cats[i-shift] = cats[i];
} else {
shift++;
}
}
used -= shift;
// Copy any new +load candidates from the new list to the detached list.
new_categories_added = (loadable_categories_used > 0);
for (i = 0; i < loadable_categories_used; i++) {
if (used == allocated) {
allocated = allocated*2 + 16;
cats = (struct loadable_category *)
realloc(cats, allocated *
sizeof(struct loadable_category));
}
cats[used++] = loadable_categories[i];
}
// Destroy the new list.
if (loadable_categories) free(loadable_categories);
// Reattach the (now augmented) detached list.
// But if there's nothing left to load, destroy the list.
if (used) {
loadable_categories = cats;
loadable_categories_used = used;
loadable_categories_allocated = allocated;
} else {
if (cats) free(cats);
loadable_categories = nil;
loadable_categories_used = 0;
loadable_categories_allocated = 0;
}
if (PrintLoading) {
if (loadable_categories_used != 0) {
_objc_inform("LOAD: %d categories still waiting for +load\n",
loadable_categories_used);
}
}
return new_categories_added;
}
之前肠鲫,我們?cè)诳碈ategory的時(shí)候,當(dāng)類文件和分類文件中寫了同名方法時(shí)或粮,我們調(diào)用方法后导饲,分析得出只調(diào)用了分類中的方法(多個(gè)分類只調(diào)用一個(gè))。這是因?yàn)檎{(diào)用方法時(shí)利用的是objc_sendMsg的機(jī)制其調(diào)用函數(shù)的氯材。
現(xiàn)在明顯可以看出load方法和其他方法的不同渣锦,調(diào)用load方法本就在runtime加載時(shí),是通過找到load方法存儲(chǔ)的內(nèi)存地址調(diào)用的氢哮,所以每個(gè)class的load方法都會(huì)被調(diào)用袋毙。
父類、子類中的load方法
還是來看一下源碼:
obje-runtime-new.mm文件中有個(gè)prepare_load_methods方法命浴,在這里面schedule_class_load這句代碼對(duì)應(yīng)的是處理load方法時(shí)對(duì)class文件的加載順序處理:
- 可以看出在處理自己的之前娄猫,會(huì)先處理自己的父類
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
//在處理自己之前, 遞歸調(diào)用,傳入的參數(shù)是自己的父類
schedule_class_load(cls->superclass);
//將cls(類)添加到loadable_classes數(shù)組中(加入的新class在數(shù)組后面)
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
load方法的總結(jié)
load方法會(huì)在runtime加載類贱除、分類時(shí)調(diào)用
每個(gè)類生闲、分類的+load,在程序運(yùn)行過程中只調(diào)用一次
-
調(diào)用順序
先調(diào)用類的+load(先編譯月幌,先調(diào)用)
調(diào)用子類的+load之前會(huì)先調(diào)用父類的+load
再調(diào)用分類的+load(先編譯碍讯,先調(diào)用)
也就是說:
1.當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
2.當(dāng)子類未實(shí)現(xiàn)load方法時(shí),不會(huì)調(diào)用父類load方法
3.類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
4.當(dāng)有多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會(huì)執(zhí)行,但執(zhí)行順序不確定(取決于分類文件的編譯順序)
2、initialize
initialize方法是在類第一次接受到消息時(shí)調(diào)用扯躺,必須是類使用到了才會(huì)被調(diào)用捉兴。它是通過發(fā)送objc_sendMsg的機(jī)制的方式去調(diào)用的。
類录语、分類中的initialize方法
在學(xué)習(xí)category的時(shí)候倍啥,我們已經(jīng)知道如果是使用objc_sendMsg的機(jī)制去調(diào)用方法的話,都是會(huì)先調(diào)用分類的同名方法澎埠,所以如果分類實(shí)現(xiàn)了+initialize方法虽缕,就覆蓋類本身的+initialize方法。
父類蒲稳、子類中的initialize方法
如果子類沒有實(shí)現(xiàn)+initialize氮趋,會(huì)調(diào)用父類的+initialize(所以父類的+initialize可能會(huì)被調(diào)用多次)伍派。
因?yàn)樵创a太過分散,我就不貼源碼了剩胁,采用偽代碼的方法簡(jiǎn)單表示一下:
//以上說明是建立在class本身或者class的分類實(shí)現(xiàn)了initialize方法的基礎(chǔ)上诉植。
//如果class本身沒有實(shí)現(xiàn)initialize方法,且其分類中也沒有實(shí)現(xiàn)initialize方法昵观,
//那么就會(huì)沿著其父類向上尋找晾腔,一直找到實(shí)現(xiàn)了initialize方法的父類,調(diào)用該父類的initialize方法
if(class本身沒有被初始化){
if(class有父類 && 父類沒有被初始化){
if(class的父類有分類 && 分類中實(shí)現(xiàn)了initialize方法){
調(diào)用class的父類的分類中的initialize方法
}else{
調(diào)用class的父類中的initialize方法
}
}else{
if(class有分類 && 分類中實(shí)現(xiàn)了initialize方法){
調(diào)用class的分類中的initialize方法
}else{
調(diào)用class本身的initialize方法
}
}
}
initialize方法的總結(jié)
+initialize方法會(huì)在類第一次接收到消息時(shí)調(diào)用
-
調(diào)用順序
先調(diào)用父類的+initialize啊犬,再調(diào)用子類的+initialize(先初始化父類建车,再初始化子類,每個(gè)類只會(huì)初始化1次)
類和分類中都含有+initialize時(shí)椒惨,調(diào)用分類中的+initialize方法
也就是說:
1.父類的initialize方法會(huì)比子類先執(zhí)行
2.當(dāng)子類未實(shí)現(xiàn)initialize方法時(shí),會(huì)調(diào)用父類initialize方法,子類實(shí)現(xiàn)initialize方法時(shí),會(huì)覆蓋父類initialize方法缤至。
3.當(dāng)有多個(gè)Category都實(shí)現(xiàn)了initialize方法,會(huì)覆蓋類中的方法,只執(zhí)行最后一個(gè)(取決于分類文件的編譯順序)
3、總結(jié)
<1>Category中有l(wèi)oad方法嗎康谆?load方法是什么時(shí)候調(diào)用的领斥?load 方法能繼承嗎?
有l(wèi)oad方法
load方法在runtime加載類沃暗、分類的時(shí)候調(diào)用
load方法不遵守繼承規(guī)則月洛,一般情況下不會(huì)主動(dòng)去調(diào)用load方法,都是讓系統(tǒng)自動(dòng)調(diào)用
<2>load孽锥、initialize方法的區(qū)別什么嚼黔?
-
調(diào)用方式
load是根據(jù)函數(shù)地址直接調(diào)用的
initizlize是通過objc_sendMsg調(diào)用 -
調(diào)用時(shí)刻
load是runtime加載類、分類的時(shí)候調(diào)用(只會(huì)調(diào)用一次)
initizlize是類第一次接收到消息的時(shí)候調(diào)用惜辑,每一個(gè)類只會(huì)initizlize一次(父類的initizlize方法可能會(huì)被調(diào)用多次唬涧,但是這是因?yàn)樽宇悰]有實(shí)現(xiàn)initizlize方法,objc_sendMsg方法通過isa找到父類的同名方法)
<3>load盛撑、initialize方法在category中的調(diào)用的順序碎节?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過程?
-
load
1>先調(diào)用類的load
? a)先編譯的類優(yōu)先調(diào)用load方法
? b)調(diào)用子類的load方法之前抵卫,會(huì)先調(diào)用父類的load方法
2>再去調(diào)用分類的load
? a)先編譯的分類狮荔,優(yōu)先調(diào)用load -
initizlize
1>先初始化父類
? a)有分類中實(shí)現(xiàn)了initizlize,調(diào)用先編譯的分類的initizlize方法
2>再初始化子類(子類如果沒有實(shí)現(xiàn)initizlize介粘,會(huì)調(diào)用到父類的initizlize方法)
? a)有分類中實(shí)現(xiàn)了initizlize殖氏,調(diào)用先編譯的分類的initizlize方法