每一個類,每一個分類中都存在一個
+(void)load
方法。咱們不須要顯式的進行調用,當runtime動態加載類、分類的時候會進行調用。數組
建立一個command line 項目 建立幾個類。Student繼承自Person,每一個類包括其Category中都實現load方法,運行咱們發現控制檯打印瞭如下信息。函數
咱們發現類中的load方法沒有被Category中的load方法覆蓋,而是全都進行了調用。這是爲何呢?另外load方法的調用順序有什麼規律呢?指針
經過runtime源碼咱們能夠看到在runtime的入口函數void _objc_init(void)
(存在於objc_os.mm中)中一段代碼加載images(鏡像)。_dyld_objc_notify_register(&map_images, load_images, unmap_image);
日誌
進入load_images能夠發現有調用load方法的函數code
經過函數名能夠知道這裏實現對load方法展開調用。查看該方法的實現,咱們發現它又是經過call_class_loads對類的load方法進行調用,經過call_category_loads對category中的load方法進行調用的。它使用了一個while循環首先調用類的load方法,類的load方法所有調用完成以後,再調用Category的load方法。cdn
查看call_class_load函數的實現,咱們發現它經過遍歷一個數組獲取一個結構體loadable_class。blog
這裏的method就是load函數的地址,而call_class_loads獲取函數地址,直接進行調用。call_category_loads也是如此。繼承
那麼loadable_classes這個list是按照什麼規律生成的呢?遞歸
從新觀察load_iamges函數,咱們發現有一個prepare_load_methods函數ssl
這個方法遍歷classlist,經過schedule_class_load
函數準備class的load方法。這個函數中有遞歸調用了本身,首先處理父類,最後調用了add_class_to_loadable_list
函數,將load生成loadable_classes
數組和loadable_classes_used
可調用的load方法的數量。
經過這個函數的實現,咱們也能夠驗證上文中所說的loadable_class
中的method就是load方法,咱們能夠看到method是經過調用getLoadMethod
獲取並添加到結構體loadable_class
中的。
Category的將load方法加載到數組的方法跟class基本一致,可是Category是按照編譯順序進行添加的。
綜上所述:
+(void)load
方法是在runtime加載類或分類的時候調用的。loadable_classes_used
數量會置爲0。initialize
在一個類剛剛接收到消息的時候進行調用。利用上面的代碼,實現initialize
方法,在main函數中讓student給alloc發消息。獲得如下打印:
通過上面的經驗能夠得知,initialize是經過objc_msgSend方法調用的,因此只打印了category的initialize中的日誌。而category的調用,根據Category的底層實現能夠得知,後編譯的被調用了。
因爲objc_msgSend的實現沒有開源,因此經過查看objc-runtime-new.mm中的獲取實例方法的函數(class_getInstanceMethod
)進行窺探。
在該方法中經過調用lookUpImpOrNil
方法查找方法的實現,這個方法又調用了lookUpImpOrForward
方法繼續查找。
在lookUpImpOrForward
方法中若是存在initialize
而且沒有調用過,則經過_class_initialize (_class_getNonMetaClass(cls, inst));
調用initialize。
_class_initialize (_class_getNonMetaClass(cls, inst));
中經過遞歸先經過callInitialize(cls);
調用父類的initialize。
callInitialize(cls);
中就能夠看出是經過objc_msgSend
調用的。
因此會執行經過isa指針查找方法實現的過程。
+(void)initialize
是經過objc_msgSend調用的+(void)initialize
,因此父類的+(void)initialize
方法會被調用屢次。