Category 原理源碼分析 load、initialize方法

本節(jié)將解釋下一下問題:

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

2.Category跟Extension的區(qū)別?

3.Category有l(wèi)oad方法么?父類子類Category之間調(diào)用順序是什么?

4.load、initialize方法的區(qū)別,調(diào)用順序是什么?以及出現(xiàn)繼承他們的調(diào)用順序是什么?

5.Categoryn能添加成員變量么?如果可以,如何添加?

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


創(chuàng)建一個(gè)Category

首先可以將OC轉(zhuǎn)換成C語言編譯文件查看一下,當(dāng)前目錄下(注意方法要實(shí)現(xiàn))

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc NSObject+Test.m -o?NSObject+Test.cpp


Category轉(zhuǎn)換成C語言編譯

原理:?從編譯文件我們可以看出,Category編譯之后底層會轉(zhuǎn)換成一個(gè)_category_t的結(jié)構(gòu)體,內(nèi)部包含著Category的對象方法,類方法,屬性,協(xié)議信息。在運(yùn)行時(shí)期,會經(jīng)過runtime合并到類信息中(類方法放置元類對象中)。 PS: 類方法存儲方式,可以看我之前的文章Class本質(zhì)

下面我們就來搞下:runtime是如何將Category中的信息,合并到對應(yīng)類信息中呢?

runtime源碼地址

下載runtime的源碼:objc-os.mm這個(gè)文件是入口文件,內(nèi)部void_objc_init(void)方法是初始化方法


runtime初始化

內(nèi)部調(diào)用map_images函數(shù) (讀取鏡像/模塊),進(jìn)入函數(shù),在進(jìn)入map_images_nolock函數(shù)實(shí)現(xiàn),再進(jìn)入_read_images函數(shù),(從函數(shù)命也能夠看出來 到了讀取模塊的地方了)


開始讀取Category

在進(jìn)入重新布局class方法,內(nèi)部調(diào)用了attachCategories方法,追加類別

開始追加Category


解析Category方法內(nèi)部實(shí)現(xiàn)

然后進(jìn)入attachLists函數(shù)調(diào)用。


Category追加到當(dāng)前類

2.Category跟Extension的區(qū)別?

class extension 是在編譯前就儲存在類信息中了,而Category是在運(yùn)行時(shí)才會被載入類信息中的

Category是在運(yùn)行時(shí)才會被合并到類信息中去的。

3.Category有l(wèi)oad方法么?父類子類Category之間調(diào)用順序是什么?

load方法特點(diǎn):是在runtime裝載類/Category的時(shí)候調(diào)用,不管你是否用的該類、Category都會調(diào)用load方法,runtime是采用的是直接找到對應(yīng)類的load函數(shù)地址調(diào)用,而并非是用objc_msgsend()來調(diào)用。

調(diào)用順序是先調(diào)用類的load方法,再調(diào)用Category的load方法,如果類中存在繼承關(guān)系,先調(diào)用super class的load方法在調(diào)用子類的load方法。

源碼來看:(objc-os.mm-->load_images-->call_load_methods())


load方法加載順序
真正調(diào)用class的load方法

從圖中可以看出,先進(jìn)行循環(huán)class的load方法執(zhí)行,在進(jìn)行Category方法的執(zhí)行。

那么繼承關(guān)系又是怎么調(diào)用的呢?同樣我們也可以通過源碼來分析。

源碼路徑:(objc-os.mm-->load_images-->prepare_load_methods()-->schedule_class_load)


預(yù)加載class列表

可以看到apple用遞歸調(diào)用,然后每次傳入自身父類,生成的數(shù)組是@[@(super class),@(son class)]這種類型,而且我們從上上圖 我們也可以看出,循環(huán)數(shù)組是從i=0開始的,也就意味著父類的調(diào)用順序會比子類要早。

總結(jié):load方法的調(diào)用順序是:??

1.先加載class中的load方法。

? ? ? ?> 存在繼承關(guān)系的類,會先進(jìn)行加載父類,在加載子類,比如person.h、 son.h(son繼承person)會先加載person的load方法

? ? ? ?>不存在繼承關(guān)系的類,會根據(jù)編譯順序來加載load方法。比如dog.h 、cat.h兩個(gè)類的編譯順序誰在先先調(diào)用。(編譯順序工程>Tagets>build phases >complie sources? 可以看到編譯順序)

2.再進(jìn)行加載Category中的load方法

? ? ? ? ?>會根據(jù)編譯順序來加載load方法。比如NSObject +dog.h 、NSObject +cat.h兩個(gè)分類的編譯順序誰在先先調(diào)用。(編譯順序工程>Tagets>build phases >complie sources? 可以看到編譯順序)

4.load、initialize方法的區(qū)別,調(diào)用順序是什么?以及出現(xiàn)繼承他們的調(diào)用順序是什么?

initialze 方法是在對象第一次接受消息的時(shí)候調(diào)用,采用的是objc_msgSend()消息轉(zhuǎn)發(fā)機(jī)制,

而且每個(gè)類只調(diào)用一次,父類優(yōu)先調(diào)用,父類存在列表,并且實(shí)現(xiàn)的該方法,根據(jù)消息轉(zhuǎn)發(fā)機(jī)制,類別調(diào)用順序優(yōu)于類對象,所以會先調(diào)用父類類別。

比如現(xiàn)在有兩個(gè)類,person 、student? 繼承關(guān)系,還有兩個(gè)類別,person+eat 、student+eat、四個(gè)文件的都實(shí)現(xiàn)了initialize方法,當(dāng)發(fā)生[student alloc]的時(shí)候,也就是對象第一次接受纖細(xì)的時(shí)候,會先從父類中尋找,superclass的方法列表中,分類優(yōu)于對象,所以執(zhí)行的是person+eat 中的initialize方法,進(jìn)而在調(diào)用student中的initialize方法,相同道理,會調(diào)用student+eat中的initialze方法。

load、initialze區(qū)別在于實(shí)現(xiàn)方式不同,load是直接找到load函數(shù)地址來調(diào)用,而initialze是采用消息轉(zhuǎn)發(fā)機(jī)制來進(jìn)行調(diào)用。因?yàn)閕nitialze是通過objc_msgSend進(jìn)行調(diào)用的,所以會有以下特點(diǎn):如果子類沒有實(shí)現(xiàn),會調(diào)用父類的initialze,所以父類的initialze可能被調(diào)用多次,如果分類實(shí)現(xiàn)了initialze,就會覆蓋類本身的initialze調(diào)用。

總結(jié):load、initialze的區(qū)別總結(jié)?

1》調(diào)用方式:load是根據(jù)函數(shù)地址直接到調(diào)用。initialze是通過objc_msgSend()調(diào)用。

2》調(diào)用時(shí)刻:load是runtime加載類,Category的時(shí)候調(diào)用,只會調(diào)用一次,而initialze是在類第一次接受到消息的時(shí)候調(diào)用,每個(gè)類只會initialze一次,(父類的initialze可能會被多次調(diào)用)

3》load調(diào)用順序:

a>? load 先調(diào)用類的load,先編譯的類優(yōu)先調(diào)用load方法,調(diào)用子類的load之前,會調(diào)用父類的load

b>?再調(diào)用分類的load

4》initialze調(diào)用順序:

a>?先初始化父類

b>?再初始化子類,可能最終調(diào)用的父類的initialze方法。

5.Categoryn能添加成員變量么?如果可以,如何添加?

不能直接添加成員變量,因?yàn)镃ategory中添加屬性不會自動(dòng)生成 _成員變量 、setter、getter方法的實(shí)現(xiàn),只會生成setter、getter方法的聲明。但是可以用運(yùn)行時(shí)間接關(guān)聯(lián)對象,完成添加屬性。

運(yùn)用runtime? 關(guān)聯(lián)對象的api,可以達(dá)到屬性關(guān)聯(lián)的目的。這里并不是說類別就可以添加屬性了,而是通過runtime達(dá)到跟添加屬性一樣的目的而已。


關(guān)聯(lián)屬性

objc_setAssociatedObject(<#id? _Nonnull object#>, <#const void * _Nonnull key#>, <#id? _Nullable value#>, <#objc_AssociationPolicy policy#>)

三個(gè)參數(shù),第一個(gè)是當(dāng)前對象,第二個(gè)是一個(gè)常亮指針。這里面用@selector(age) 返回的是一個(gè)唯一的age的函數(shù)指針地址,可以作為常亮。

第三個(gè)?

第三個(gè)參數(shù)

根據(jù)你參數(shù)定義的關(guān)鍵字選擇對應(yīng)的枚舉值即可

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評論 2 373

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