ObjC load与initialize 简析
+ (void)load
每個類,每個分類中都存在一個+(void)load方法。我們不需要顯式的進行調用,當runtime動態加載類、分類的時候會進行調用。
創建一個command line 項目 創建幾個類。Student繼承自Person,每個類包括其Category中都實現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方法的函數
通過函數名可以知道這里實現對load方法展開調用。查看該方法的實現,我們發現它又是通過call_class_loads對類的load方法進行調用,通過call_category_loads對category中的load方法進行調用的。它使用了一個while循環首先調用類的load方法,類的load方法全部調用完成之后,再調用Category的load方法。
查看call_class_load函數的實現,我們發現它通過遍歷一個數組獲取一個結構體loadable_class。
這里的method就是load函數的地址,而call_class_loads獲取函數地址,直接進行調用。call_category_loads也是如此。
那么loadable_classes這個list是按照什么規律生成的呢?
重新觀察load_iamges函數,我們發現有一個prepare_load_methods函數
這個方法遍歷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的一些問題
綜上所述:
- +(void)load方法是在runtime加載類或分類的時候調用的。
- 每個類分類的load在程序運行過程中只會調用一次。調用的完成loadable_classes_used數量會置為0。
- 先調用類的load方法(先編譯的先調用)父類load優先調用
- 分類的load方法 先編譯的先調用
- load方法是直接查找到函數地址進行調用的而不是通過消息發送機制調用的,所以不會出現方法覆蓋
+(void)initialize
initialize在一個類剛剛接收到消息的時候進行調用。利用上面的代碼,實現initialize方法,在main函數中讓student給alloc發消息。得到以下打印:
經過上面的經驗可以得知,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_getNonMetaClass(cls, inst));中通過遞歸先通過callInitialize(cls);調用父類的initialize。
callInitialize(cls);中就可以看出是通過objc_msgSend調用的。
所以會執行通過isa指針查找方法實現的過程。
+(void)initialize的一些問題
- +(void)initialize是通過objc_msgSend調用的
- category實現了initialize方法會覆蓋父類的initilize方法
- initialize也只初始化一次,但是由于是通過objc_msgSend的調用的,如果子類沒有實現initialize,而父類實現了,則子類每次初始化的時候都會調用父類的+(void)initialize,所以父類的+(void)initialize方法會被調用多次。
總結
以上是生活随笔為你收集整理的ObjC load与initialize 简析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 02 面向对象之:类空间问题以及类之间的
- 下一篇: 点量OTT TV 点播软件模式为何受海外