日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ObjC load与initialize 简析

發(fā)布時間:2025/6/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ObjC load与initialize 简析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

+ (void)load

每個類,每個分類中都存在一個+(void)load方法。我們不需要顯式的進(jìn)行調(diào)用,當(dāng)runtime動態(tài)加載類、分類的時候會進(jìn)行調(diào)用。

創(chuàng)建一個command line 項目 創(chuàng)建幾個類。Student繼承自Person,每個類包括其Category中都實現(xiàn)load方法,運行我們發(fā)現(xiàn)控制臺打印了以下信息。

我們發(fā)現(xiàn)類中的load方法沒有被Category中的load方法覆蓋,而是全都進(jìn)行了調(diào)用。這是為什么呢?另外load方法的調(diào)用順序有什么規(guī)律呢?

runtime 源碼-窺探load方法的調(diào)用

通過runtime源碼我們可以看到在runtime的入口函數(shù)void _objc_init(void)(存在于objc_os.mm中)中一段代碼加載images(鏡像)。_dyld_objc_notify_register(&map_images, load_images, unmap_image);

進(jìn)入load_images可以發(fā)現(xiàn)有調(diào)用load方法的函數(shù)

通過函數(shù)名可以知道這里實現(xiàn)對load方法展開調(diào)用。查看該方法的實現(xiàn),我們發(fā)現(xiàn)它又是通過call_class_loads對類的load方法進(jìn)行調(diào)用,通過call_category_loads對category中的load方法進(jìn)行調(diào)用的。它使用了一個while循環(huán)首先調(diào)用類的load方法,類的load方法全部調(diào)用完成之后,再調(diào)用Category的load方法。

查看call_class_load函數(shù)的實現(xiàn),我們發(fā)現(xiàn)它通過遍歷一個數(shù)組獲取一個結(jié)構(gòu)體loadable_class。

這里的method就是load函數(shù)的地址,而call_class_loads獲取函數(shù)地址,直接進(jìn)行調(diào)用。call_category_loads也是如此。

那么loadable_classes這個list是按照什么規(guī)律生成的呢?

重新觀察load_iamges函數(shù),我們發(fā)現(xiàn)有一個prepare_load_methods函數(shù)

這個方法遍歷classlist,通過schedule_class_load函數(shù)準(zhǔn)備class的load方法。這個函數(shù)中有遞歸調(diào)用了自己,首先處理父類,最后調(diào)用了add_class_to_loadable_list函數(shù),將load生成loadable_classes數(shù)組和loadable_classes_used可調(diào)用的load方法的數(shù)量。

通過這個函數(shù)的實現(xiàn),我們也可以驗證上文中所說的loadable_class中的method就是load方法,我們可以看到method是通過調(diào)用getLoadMethod獲取并添加到結(jié)構(gòu)體loadable_class中的。

Category的將load方法加載到數(shù)組的方法跟class基本一致,但是Category是按照編譯順序進(jìn)行添加的。

+ (void)load的一些問題

綜上所述:

  • +(void)load方法是在runtime加載類或分類的時候調(diào)用的。
  • 每個類分類的load在程序運行過程中只會調(diào)用一次。調(diào)用的完成loadable_classes_used數(shù)量會置為0。
  • 先調(diào)用類的load方法(先編譯的先調(diào)用)父類load優(yōu)先調(diào)用
  • 分類的load方法 先編譯的先調(diào)用
  • load方法是直接查找到函數(shù)地址進(jìn)行調(diào)用的而不是通過消息發(fā)送機(jī)制調(diào)用的,所以不會出現(xiàn)方法覆蓋

+(void)initialize

initialize在一個類剛剛接收到消息的時候進(jìn)行調(diào)用。利用上面的代碼,實現(xiàn)initialize方法,在main函數(shù)中讓student給alloc發(fā)消息。得到以下打印:

經(jīng)過上面的經(jīng)驗可以得知,initialize是通過objc_msgSend方法調(diào)用的,所以只打印了category的initialize中的日志。而category的調(diào)用,根據(jù)Category的底層實現(xiàn)可以得知,后編譯的被調(diào)用了。

runtime 源碼-窺探initialize方法的調(diào)用

由于objc_msgSend的實現(xiàn)沒有開源,所以通過查看objc-runtime-new.mm中的獲取實例方法的函數(shù)(class_getInstanceMethod)進(jìn)行窺探。

在該方法中通過調(diào)用lookUpImpOrNil方法查找方法的實現(xiàn),這個方法又調(diào)用了lookUpImpOrForward方法繼續(xù)查找。

在lookUpImpOrForward方法中如果存在initialize并且沒有調(diào)用過,則通過_class_initialize (_class_getNonMetaClass(cls, inst));調(diào)用initialize。

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

callInitialize(cls);中就可以看出是通過objc_msgSend調(diào)用的。

所以會執(zhí)行通過isa指針查找方法實現(xiàn)的過程。

+(void)initialize的一些問題

  • +(void)initialize是通過objc_msgSend調(diào)用的
  • category實現(xiàn)了initialize方法會覆蓋父類的initilize方法
  • initialize也只初始化一次,但是由于是通過objc_msgSend的調(diào)用的,如果子類沒有實現(xiàn)initialize,而父類實現(xiàn)了,則子類每次初始化的時候都會調(diào)用父類的+(void)initialize,所以父類的+(void)initialize方法會被調(diào)用多次。
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的ObjC load与initialize 简析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。