我們都知道,App啟動時,代碼中+(void)load方法早于main函數調用,main函數是我們程式的入口,那麼我們程式在main函數之前,都做了些什麼工作呢?今天我們一起去探讨一下main函數之前的流程!
首先我們的應用程式,會依賴很多的庫-----被作業系統寫入記憶體的可執行代碼的二進制!
庫又有靜态庫如.a檔案和動态庫如.framework
- App啟動之後,main函數加載之前的過程:
- dyld::_main的流程
- 上圖中的兩張圖檔
那麼,我們我們接下來看_objc_init()之後的調用流程及類是如何加載到記憶體的!
- _objc_init(): 引導程式初始化,注冊我們的鏡像通知與dyld.在庫初始化之前被libSystem調用!
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(); //關于線程key的綁定 例如析構函數設定靜态密鑰key //運作c++靜态構造函數,libc在dyld調用靜态構造函數之前調用_objc_init(), //是以我們必須自己做. static_init(); lock_init();//空實作 //初始化libobjc的異常處理系統,由map_images()調用 exception_init(); //注冊鏡像加載通知回調 /** 注:僅供objc運作時使用 映射、未映射和初始化objc映像時要調用的寄存器處理程式 Dyld将使用包含objc-image-info部分的鏡像數組調用“mapped”函數 那些是dylib的鏡像會自動替換ref計數,是以objc不再需要對它們調用dlopen()來防止它們被解除安裝 在調用_dyld_objc_notify_register()期間,dyld将調用已經加載了objc鏡像的“映射”函數 在以後的dlopen()調用期間,dyld還将調用“mapped”函數 當Dyld在該鏡像中被稱為初始化器時,Dyld将調用“init”函數 這是objc調用圖像中的任何+load方法的時候 */ _dyld_objc_notify_register(&map_images, load_images, unmap_image); }
- map_images分析 我們上面思維導圖分析了map_images的流程,大緻了解了類的讀取,初始化,映射等的操作,下面我們看看load_images對類的加載!
在講load_images方法之前,我們需要了解一些知識點:
- 懶加載類與非懶加載類
- 在read_images()思維導圖中的:對所有類進行重映射步驟中,在源碼中有一段注釋這麼寫的: Realize non-lazy classes (for +load methods and static instances).有道翻譯:實作非惰性類(用于+load方法和靜态執行個體).我們可以看出,在read_images()中,初始化的都是非懶加載的類,既然有非懶加載的類,那麼就應該有懶加載的類,那麼非懶加載與懶加載類是怎麼區分的呢?懶加載的類又是什麼時候初始化的呢?
- 非懶加載類在編譯期就進行編譯連結實作了,而懶加載類是在我們用到的時候才去調用,初始化!這兩種類的區分方法就是:看這個類的實作中是否實作了+load方法!!!
- 如果我們在類中實作了+load方法,那麼這個類在編譯期就會進行初始化實作,沒有實作load方法,那麼這個類在read_images()中就不會進行初始化,映射等的操作!
- 那麼.懶加載的類的調用時機,我們在這裡提一下,後期會講到這個!
- 我的部落格裡有兩篇是講runtime的博文,其中我們有一個方法印象深刻:消息查找或轉發:lookupimporforward:
//此處就是懶加載類的調用,沒有實作的類就會走到這裡(消息轉發過程調用!!!) //實作這個懶加載的類,非懶加載的類(編譯期實作的)是不會走下面的流程的. if (!cls->isRealized()) { cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); } static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) { return realizeClassMaybeSwiftMaybeRelock(cls, lock, true); } static Class realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked) { lock.assertLocked(); if (!cls->isSwiftStable_ButAllowLegacyForNow()) {//OC // Non-Swift class. Realize it now with the lock still held. //傳回類的實際類結構.就是進行類的初始化操作!!! realizeClassWithoutSwift(cls); if (!leaveLocked) lock.unlock(); } else {//swift // Swift class. We need to drop locks and call the Swift // runtime to initialize it. lock.unlock(); cls = realizeSwiftClass(cls); assert(cls->isRealized()); // callback must have provoked realization if (leaveLocked) lock.lock(); } return cls; }
- 是以.下面的load_images()也是對已經加入到類清單的類進行load操作!(不包含懶加載的類!)
- load_images分析:
void load_images(const char *path __unused, const struct mach_header *mh) { //hasLoadMethods判斷是否實作了+load方法,沒有直接傳回 //也就是說,我們加載的是實作了load方法的類(系統的一些類我們不去管) // 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 //找到實作了+load方法的類并加入到一個表,友善操作,load方法調用完成,這個表會被釋放. { mutex_locker_t lock2(runtimeLock); prepare_load_methods((const headerType *)mh); } // Call +load methods (without runtimeLock - re-entrant) //調用 call_load_methods(); }
上面我們經過分析,知道load方法的調用時機以及是如何調用的!
經過上面的流程,非懶加載類及分類的加載完成,以及load方法的調用時機以及是如何調用的,_objc_init()方法至此調用完成!下一篇我們會談談類的拓展、類拓展與分類的差別、類與分類的關系,以及分類屬性關聯、initialize方法調用時機等内容!
謝謝觀賞!