前言:git
上篇文章 iOS Category底層原理詳細研究流程 中 , 咱們有說到編譯時
dyld
解讀Mach-o
文件對應在runtime
庫的入口函數_objc_init
中三種不一樣時期對應的回調函數.github那麼一樣 , 咱們探索
load
方法也是由此展開 . 也就是在dyld
讀取完成時會調用load_images
方法 ,load
方法也是在此時調用. 感興趣的能夠去解讀一下上篇博客.數組
廢話很少說 , 一樣打開 objc4
的源碼 ( git開源地址 )app
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
複製代碼
關於 map_images
, load_images
, unmap_image
這三個函數我上一篇博客有具體提到, 這裏很少贅述了. 直接進去 load_images
:函數
void load_images(const char *path __unused, const struct mach_header *mh) {
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
/** 準備 */
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
複製代碼
代碼中看出這裏分爲了兩步 , 先準備 , 而後調用 . --> GO
post
void prepare_load_methods(const headerType *mhdr) {
size_t count, i;
runtimeLock.assertLocked();
//從 Macho 文件加載類的列表
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
//數組:[<cls,method>,<cls,method>,<cls,method>] 有順序
schedule_class_load(remapClass(classlist[i]));
}
//針對分類的操做!
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
複製代碼
這裏又有兩步操做spa
//遞歸調用
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
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
複製代碼
其實就是從 Mach-o
中讀取到全部的類的列表 . 而後ssr
superclass
指針進行排列. 以此來知足一個規則 :一個類的
load
方法調用 , 必定是在其父類的load
方法以後.指針
NSObject
後 , 再找父類爲 nil
, 跳出遞歸 . 結束排列 , 而且將每一個 load
方法的 cls
和 method
存儲到全局的 loadable_class
結構體中.
分類的 load
大致流程和類的基本相似 , 一樣先從 Mach-o
讀取全部的分類列表 , 而後直接遍歷 , 添加分類的 load
方法的 cls
和 method
存儲到全局的 loadable_categories
結構體中.code
也就是說 , 不一樣分類之間 load
方法的加載順序是根據 Mach-o
文件的編譯順序有關.
call_load_methods
:
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) {
//先調用類的 load 方法
call_class_loads();
}
// 2. Call category +loads ONCE
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;
}
複製代碼
這裏就比較簡單了. 先遍歷以前存儲好的類的 load
方法列表. 依次調用. 而後遍歷分類的列表 , 依次調用.
load
方法在runtime
庫開始運行時調用.- 一個類的
load
方法在全部的父類load
方法調用以後.- 分類的
load
方法在類的load
方法以後.- 不一樣分類之間的
load
方法調用順序和編譯順序有關.