iOS程式載入過程
- 在程式啟動之後,由dyld(動態連結編輯器)將程式載入到二進位制中,完成一些檔案的初始化操作。
- Runtime向dyld中註冊回撥函式。
- 通過ImageLoader將所有的image載入到記憶體中。
- dyld在images發生改變時主動呼叫回撥函式。
- Runtime在接收到回撥函式後開始執行map_images,load_images等函式,並回撥+load函式。
- 呼叫main()函式,開始執行業務程式碼。
map_images
在Runtime載入過程中,會呼叫_objc_init函式,在_objc_init函式中主要呼叫了map_images,load_images等方法
void _objc_init(void) {
// .... 各種init
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
//主要作用是一個呼叫中轉
void map_images(unsigned count, const char * const paths[], const struct mach_header * const mhdrs[])
{
rwlock_writer_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
}
複製程式碼
read_images主要邏輯:
- 載入所有類到gbd_objc_readlized_classes表中
- 對所有類做重對映
- 將所有SEL都註冊到namedSelectors表中
- 修復函式指標遺留
- 將所有的Protocol都新增到protocol_map表中
- 多所有的Protocol做重對映
- 初始化所有非懶載入的類,進行rw,ro操作
- 遍歷已標記的懶載入的類,並做初始化操作
- 處理所有Category,包括Class和Meta Class
- 初始化所有未初始化的類
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unopt imizedTotalClasses)
{
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil;
size_t resolvedFutureClassCount = 0;
static bool doneOnce;
TimeLogger ts(PrintImageTimes);
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
if (!doneOnce) {
doneOnce = YES;
//例項化儲存類的雜湊表,並根據當前類數量做動態擴容
int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
//由編輯器讀取類列表,並將所有的類新增到雜湊表中,並標記懶載入的類並初始化空間
for (EACH_HEADER) {
if (! mustReadClasses(hi)) {
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
/** 將新類新增到雜湊表中 */
// 從編譯後的類列表中取出所有類,獲取到一個classref_t型別指標
classref_t *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
// 陣列中取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunLoop等系統類<CF、Fundation、liddispatch等>和自己建立的類
Class cls = (Class)classlist[i];
// 通過readClass函式獲取處理過後的新類,內部主要操作ro和rw結構體
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 初始化所有懶載入的類需要的記憶體空間
if (newCls != cls && newCls) {
// 將懶載入的類新增到陣列中
resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
//將為對映的Class和superClass重對映,被remap的類都是非懶載入的類
if (!noClassesRemapped()) {
for (EACH_HEADER) {
// 重對映Class,從_getObjc2ClassRefs函式中取出類的引用
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
// 重對映父類
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
// 對SEL做重對映
static size_t UnfixedSelectors; sel_lock();
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count); UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
//註冊SEL
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
// 修復舊的函式指標遺留
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
for (i = 0; i < count; i++) {
// 在內部將常用的alloc、objc_msgSend等函式指標進行註冊,並fix為新的函式指標
fixupMessageRef(refs+i);
}
}
// 遍歷所有協議列表,並且將協議列表載入到Protocol的雜湊表中
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
// cls = Protocol類,所有協議和物件的結構體都類似,isa對應Protocol類
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
// 獲取protocol雜湊表
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
// 從編譯器中讀取並初始化Protocol
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
// 修復協議列表應用,優化後的images不確定正確與否
for (EACH_HEADER) {
//下面的_getObjc2ProtocolRefs函式與上面的_getObjc2ProtocolRefs不同
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
// 實現非懶載入的類
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
//實現非來載入的類(例項化一些資訊,如rw)
realizeClass(cls);
}
}
// 遍歷resolvedFutureClasses陣列,實現所有懶載入的類
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
// 實現懶載入的類
realizeClass(resolvedFutureClasses[i]); resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 發現和處理所有Category
for (EACH_HEADER) {
// 外部迴圈遍歷找到當前類,查詢類對應的Category陣列
category_t **catlist = _getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
內部迴圈遍歷當前類的所有的Category
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
// 首先,通過所屬的類註冊Category。如果這個類已經被實現,則重新構造類的方法列表
bool classExists = NO;
if (cat->instanceMethods || cat->protocols || cat->instanceProperties) {
// 將Category新增到對應的value中,value是Class對應的所有的Category陣列
addUnattachedCategoryForClass(cat, cls, hi);
// 將Category的method,protocol,property新增到Class中
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
}
// 與下面邏輯相同,不過是在Meta Class中進行操作
if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) {
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
// 初始化所有的類,Category必須是最後執行
// DebugNonFragileIvars = -1,不執行
if (DebugNonFragileIvars) {
realizeAllClasses();
}
#undef EACH_HEADER
}
複製程式碼
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);
//主要查詢Class、Category的方法方法
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
// 呼叫Class、Category的方法列表
call_load_methods();
}
void prepare_load_methods(const headerType *mhdr) {
size_t count, i;
runtimeLock.assertLocked();
// 獲取到非懶載入類的列表
classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 設定Class的呼叫列表
schedule_class_load(remapClass(classlist[i]));
}
// 獲取Category的列表
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
// category for ignored weak-linked class,忽略弱連結<不懂>的類別
if (!cls) continue;
// 例項化所屬的類
realizeClass(cls);
assert(cls->ISA()->isRealized());
// 設定Categoty的呼叫列表
add_category_to_loadable_list(cat);
}
}
static void schedule_class_load(Class cls) {
if (!cls) return;
// _read_images should realize,_read_images是否完成
assert(cls->isRealized());
// 已經新增完成,則return
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering,確保superClass新增
schedule_class_load(cls->superclass);
// 新增imp、class到呼叫中
add_class_to_loadable_list(cls);
// 設定class的識別符號,表示已新增到list中
cls->setInfo(RW_LOADED);
}
// 在obj-loadmethod.mm中
void add_category_to_loadable_list(Category cat) {
IMP method;
// 獲取Category中Load方法的imp
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat);
// Don't bother if cat has no +load method,沒有則return
if (!method) return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
如果已使用大小等於陣列大小,對陣列進行動態擴容
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,loadable_categories_allocated * sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
// 準備好了loadable_calss、loadable_categories陣列,load_images會通過call_load_methods函式執行這些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) {
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;
}
// 通過遍歷獲得loadable_class結構體
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.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
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_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
struct loadable_class {
Class cls; // may be nil
IMP method;
};
複製程式碼
- 通過上述程式碼,發現load方法的呼叫順序為 父類 -> 子類 -> Category。
- category的 load方法會覆蓋 class的 load方法