ObjC load與initialize 簡析

+ (void)load

每一個類,每一個分類中都存在一個+(void)load方法。咱們不須要顯式的進行調用,當runtime動態加載類、分類的時候會進行調用。數組

實例文件目錄

建立一個command line 項目 建立幾個類。Student繼承自Person,每一個類包括其Category中都實現load方法,運行咱們發現控制檯打印瞭如下信息。函數

load信息

咱們發現類中的load方法沒有被Category中的load方法覆蓋,而是全都進行了調用。這是爲何呢?另外load方法的調用順序有什麼規律呢?指針

runtime 源碼-窺探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_images

經過函數名能夠知道這裏實現對load方法展開調用。查看該方法的實現,咱們發現它又是經過call_class_loads對類的load方法進行調用,經過call_category_loads對category中的load方法進行調用的。它使用了一個while循環首先調用類的load方法,類的load方法所有調用完成以後,再調用Category的load方法。cdn

查看call_class_load函數的實現,咱們發現它經過遍歷一個數組獲取一個結構體loadable_class。blog

loadable_class

這裏的method就是load函數的地址,而call_class_loads獲取函數地址,直接進行調用。call_category_loads也是如此。繼承

那麼loadable_classes這個list是按照什麼規律生成的呢?遞歸

從新觀察load_iamges函數,咱們發現有一個prepare_load_methods函數ssl

prepare_load_methods

這個方法遍歷classlist,經過schedule_class_load函數準備class的load方法。這個函數中有遞歸調用了本身,首先處理父類,最後調用了add_class_to_loadable_list函數,將load生成loadable_classes數組和loadable_classes_used可調用的load方法的數量。

add_class_to_loadable_list

經過這個函數的實現,咱們也能夠驗證上文中所說的loadable_class中的method就是load方法,咱們能夠看到method是經過調用getLoadMethod獲取並添加到結構體loadable_class中的。

Category的將load方法加載到數組的方法跟class基本一致,可是Category是按照編譯順序進行添加的。

+ (void)load的一些問題

綜上所述:

  • +(void)load方法是在runtime加載類或分類的時候調用的。
  • 每一個類分類的load在程序運行過程當中只會調用一次。調用的完成loadable_classes_used數量會置爲0。
  • 先調用類的load方法(先編譯的先調用)父類load優先調用
  • 分類的load方法 先編譯的先調用
  • load方法是直接查找到函數地址進行調用的而不是經過消息發送機制調用的,因此不會出現方法覆蓋

+(void)initialize

initialize在一個類剛剛接收到消息的時候進行調用。利用上面的代碼,實現initialize方法,在main函數中讓student給alloc發消息。獲得如下打印:

initialize信息

通過上面的經驗能夠得知,initialize是經過objc_msgSend方法調用的,因此只打印了category的initialize中的日誌。而category的調用,根據Category的底層實現能夠得知,後編譯的被調用了。

runtime 源碼-窺探initialize方法的調用

因爲objc_msgSend的實現沒有開源,因此經過查看objc-runtime-new.mm中的獲取實例方法的函數(class_getInstanceMethod)進行窺探。

在該方法中經過調用lookUpImpOrNil方法查找方法的實現,這個方法又調用了lookUpImpOrForward方法繼續查找。

lookUpImpOrForward方法中若是存在initialize而且沒有調用過,則經過_class_initialize (_class_getNonMetaClass(cls, inst));調用initialize。

_class_initialize

_class_initialize (_class_getNonMetaClass(cls, inst));中經過遞歸先經過callInitialize(cls);調用父類的initialize。

callInitialize(cls);中就能夠看出是經過objc_msgSend調用的。

調用objc_msgSend

因此會執行經過isa指針查找方法實現的過程。

+(void)initialize的一些問題

  • +(void)initialize是經過objc_msgSend調用的
  • category實現了initialize方法會覆蓋父類的initilize方法
  • initialize也只初始化一次,可是因爲是經過objc_msgSend的調用的,若是子類沒有實現initialize,而父類實現了,則子類每次初始化的時候都會調用父類的+(void)initialize,因此父類的+(void)initialize方法會被調用屢次。
相關文章
相關標籤/搜索