Category&load&initialize詳解

Category的底層架構(gòu)

定義在objc-runtime-new.h中


Category的底層結(jié)構(gòu)圖

分類相關(guān)信息合并到類或者元類源碼圖

源碼分類合并圖1


里面的cats 指針數(shù)組存放的是多個(gè)分類方法的二維數(shù)組磷醋,例如[[personCategory1_(Test1)_run,personCategory1_(Test1)_eat],[personCategory2_(Test2)_run,personCategory2_(Test2)_eat]]等類似情況


源碼分類合并圖2


可以看到,將分類的方法放到最前面抚官,將原來的類對(duì)象的方法或者元類方法放到最后面雁刷,這個(gè)時(shí)候如果調(diào)用了同名方法,分類的必定先于本身的類的相關(guān)方法執(zhí)行,但是如果兩個(gè)分類中都擁有同樣的方法弟胀,那么會(huì)是哪個(gè)方法先執(zhí)行呢,這個(gè)和編譯分類的先后順序有關(guān)的喊式,例如如果分類1先編譯孵户,分類2后編譯,然后兩個(gè)分類含有同樣的方法run(),那么從合并源碼圖可以看出岔留,這個(gè)賦值分類的方法是倒序遍歷夏哭,所以后編譯的放到最前面,所以后編譯的會(huì)先執(zhí)行献联,也就是會(huì)執(zhí)行分類2的run方法方庭;另外從源碼中可以看到使用了memmove,memcpy,為什么需要使用move方法呢酱固,直接copy過去效率不是更高么械念?使用move是為了確定數(shù)據(jù)的正確性,保證沒有被覆蓋运悲,后者使用cp是因?yàn)閿?shù)據(jù)是從別的地方拷貝過來龄减,不存在覆蓋一說,如果還是對(duì)這兩個(gè)方法有點(diǎn)疑惑班眯,可以參考這個(gè)鏈接

+load方法調(diào)用時(shí)機(jī)和調(diào)用方式

1.每個(gè)類希停,分類的load方法在程序運(yùn)行過程中只調(diào)用一次

2.先調(diào)用類的+load方法烁巫,按照編譯先后順序調(diào)用,先編譯先調(diào)用宠能,另外調(diào)用子類的+load之前會(huì)先調(diào)用父類的+load方法

3.再調(diào)用分類的load方法亚隙,按照編譯先后順序調(diào)用(先編譯,先調(diào)用)

下面先上傳一張測試效果圖违崇,然后再從源碼分析

load 方法調(diào)用圖

也許有人會(huì)疑惑阿弃,上面不是說是倒序遍歷,先編譯的分類羞延,方法后調(diào)用么渣淳,這里load是特殊的,因?yàn)閘oad調(diào)用和其他方法不一樣伴箩,從源碼分析:objc-os.mm->_objc_init->load_images->prepare_load_methods->schedule_class_load->add_class_to_loadable_list->add_category_loads->(*load_method)(cls,SEL_load)

load方法調(diào)用圖
類方法load調(diào)用圖
分類load方法調(diào)用圖

從上面的結(jié)果除開可以看到load方法的調(diào)用機(jī)制入愧,還可以知道load方法是根據(jù)方法地址直接調(diào)用,并不是經(jīng)過objc_msgSend函數(shù)調(diào)用


initialize方法的調(diào)用機(jī)制

+initialize 方法會(huì)在類第一次接受到消息的時(shí)候調(diào)用嗤谚,測試圖:


沒有調(diào)用


父類調(diào)用圖1


父類調(diào)用圖2
更改編譯順序圖
子類調(diào)用圖1


子類調(diào)用圖2

從上面可以得出結(jié)果是先調(diào)用父類的initialize,再調(diào)用子類的+initialize,并且每個(gè)類的初始化方法只會(huì)調(diào)用一次棺蛛,并且分類中如果實(shí)現(xiàn)了initialize方法,會(huì)覆蓋類本身的initialize巩步,這也證明了initialize是根據(jù)消息發(fā)送機(jī)制調(diào)用的鞠值,找到了就不會(huì)往下傳遞了 ,源碼尋找圖:


方法尋找圖
先調(diào)用父類
最終初始化調(diào)用圖


源碼解讀過程objc_msg_arm64.s->objc_msgSend? objc-runtime-new.mm->class_getInstanceMethod->lookUpImpOrNil->lookUpImpOrForward->_class_initialize->callInitialize->objc_msgSend(cls,SEL_initialize)

總結(jié)補(bǔ)充

1.Category實(shí)現(xiàn)原理

Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲(chǔ)著分類的對(duì)象方法渗钉,類方法彤恶,屬性,協(xié)議信息鳄橘,在程序運(yùn)行的時(shí)候声离,runtime會(huì)將Category的數(shù)據(jù)合并到類信息中(類對(duì)象,元類對(duì)象中)

2.Category和Class Extension的卻別是瘫怜?

Class Extension在編譯的時(shí)候术徊,它的數(shù)據(jù)就已經(jīng)包含在類信息里面了,而Category是在運(yùn)行時(shí)鲸湃,才會(huì)將數(shù)據(jù)合并到類信息中赠涮,所以這個(gè)時(shí)候類結(jié)構(gòu)已經(jīng)確定了,因此不能直接添加成員屬性暗挑,但是可以通過associate的全局功能進(jìn)行聲明和定義

3.Category的加載處理過程是怎樣的

通過runtime加載某個(gè)類的所有Category數(shù)據(jù)笋除,然后把所有的Category的方法,屬性炸裆,協(xié)議數(shù)據(jù)垃它,合并到一個(gè)大數(shù)組中,后面參與編譯的Category數(shù)據(jù),會(huì)在數(shù)組的前面国拇,繼續(xù)將合并后的分類數(shù)據(jù)(方法洛史,屬性,協(xié)議)插入到原來的數(shù)據(jù)的前面酱吝,這個(gè)結(jié)論可以通過源碼來解讀也殖,因?yàn)檫@些都是在運(yùn)行時(shí)合并的,所以閱讀源碼的書序务热,應(yīng)該從運(yùn)行時(shí)的過程開始理解objc-os.mm->objc_init->map_images->map_images_nolock------->objc-runtime-new.mm-->read-images->remethodizeClass->attachCategories->attachLists->realloc,memmove,memcpy


4.initialize 和 load的區(qū)別忆嗜?

+initialize是通過objc_msgSend進(jìn)行調(diào)用的,load是通過地址調(diào)用的陕习,子類沒有實(shí)現(xiàn)+initialize會(huì)調(diào)用父類的+initialize霎褐,所以父類的initialize可能會(huì)調(diào)用多次址愿,那是因?yàn)閺脑创a中可以得到该镣,如果父類沒有初始化,會(huì)調(diào)用父類响谓,然后再接著處理自己损合,因?yàn)樽约簺]有沒有這個(gè)重寫+initialze方法,所以根據(jù)runtime往父類中查找娘纷,最后找到父類或舞,另外initialize和load調(diào)用機(jī)制不一樣梁剔,如果分類了實(shí)現(xiàn)+initialize,就會(huì)覆蓋類本身的initialize的調(diào)用,load不會(huì)彤蔽,而且load方法的調(diào)用機(jī)制和原理已經(jīng)說得很清楚了

可以添加微信一起交流學(xué)習(xí):fslskz

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缸榄,隨后出現(xiàn)的幾起案子捞附,更是在濱河造成了極大的恐慌,老刑警劉巖胳嘲,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厂僧,死亡現(xiàn)場離奇詭異,居然都是意外死亡了牛,警方通過查閱死者的電腦和手機(jī)颜屠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹰祸,“玉大人甫窟,你說我怎么就攤上這事⊥苡ぃ” “怎么了蕴坪?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我背传,道長呆瞻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任径玖,我火速辦了婚禮痴脾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘梳星。我一直安慰自己赞赖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布冤灾。 她就那樣靜靜地躺著前域,像睡著了一般。 火紅的嫁衣襯著肌膚如雪韵吨。 梳的紋絲不亂的頭發(fā)上匿垄,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音归粉,去河邊找鬼椿疗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛糠悼,可吹牛的內(nèi)容都是我干的届榄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼倔喂,長吁一口氣:“原來是場噩夢啊……” “哼铝条!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起席噩,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤班缰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后班挖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲁捏,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年萧芙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了给梅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡双揪,死狀恐怖动羽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渔期,我是刑警寧澤运吓,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布渴邦,位于F島的核電站,受9級(jí)特大地震影響拘哨,放射性物質(zhì)發(fā)生泄漏谋梭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一倦青、第九天 我趴在偏房一處隱蔽的房頂上張望瓮床。 院中可真熱鬧,春花似錦产镐、人聲如沸隘庄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丑掺。三九已至,卻和暖如春述雾,著一層夾襖步出監(jiān)牢的瞬間街州,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國打工绰咽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菇肃,地道東北人地粪。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓取募,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蟆技。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玩敏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容