Runtime源碼 —— 關(guān)于category的一個(gè)問(wèn)題

關(guān)于category的文章太多了昌粤,有介紹用法的绿渣,也有介紹源碼的速种。流傳較廣的應(yīng)該算是美團(tuán)那篇深入理解Objective-C:Category

原本我已經(jīng)不打算寫了哭当,但是在我做驗(yàn)證測(cè)試的時(shí)候發(fā)現(xiàn)了一個(gè)問(wèn)題。這個(gè)問(wèn)題在我看過(guò)的其他文章中從來(lái)沒(méi)人提起過(guò)冗澈,包括美團(tuán)那篇钦勘。我花了不少時(shí)間來(lái)研究,但因?yàn)橐黄糇C的文章都找不到亚亲,所以依然不敢肯定自己的結(jié)論彻采。

現(xiàn)在我只能認(rèn)為是由于runtime的更新導(dǎo)致了不一致腐缤。這篇文章,只為記錄下這個(gè)問(wèn)題肛响。歡迎對(duì)此有更深入理解的小伙伴給我留言岭粤,不勝感激。

本文不會(huì)對(duì)category做系統(tǒng)的分析特笋,如果對(duì)category不太了解的請(qǐng)先閱讀美團(tuán)那篇文章剃浇。

結(jié)論

首先看看其他文章中給出的一個(gè)結(jié)論:

  • 分類是在運(yùn)行時(shí)被附加到相關(guān)類上的

我測(cè)試下來(lái)的結(jié)論是:

  • 對(duì)系統(tǒng)提供的類做分類,分類是在運(yùn)行時(shí)附加到相關(guān)類上的
  • 對(duì)自己創(chuàng)建的類做分類猎物,分類是在編譯期附加到相關(guān)類上的

在其他文章中虎囚,根本沒(méi)有這樣的區(qū)分,分類全部都是在運(yùn)行時(shí)附加上去的蔫磨,附加時(shí)的調(diào)用棧也很清晰:

attachCategories調(diào)用棧.png

過(guò)程

最開(kāi)始我對(duì)其他文章給出的結(jié)論是深信不疑的淘讥,因?yàn)樗械奈恼露际沁@么說(shuō)的。我只是想寫代碼做一下驗(yàn)證堤如。

為了驗(yàn)證那個(gè)結(jié)論蒲列,我創(chuàng)建了這些類:

測(cè)試分類.png
  1. 對(duì)NSObject和NSViewController創(chuàng)建的分類
  2. 對(duì)ZNObject創(chuàng)建的分類,ZNObject繼承于NSObject
  3. 對(duì)ZNViewController創(chuàng)建的分類搀罢,ZNViewController繼承于NSViewController

我在attachCategories()方法里面添加了一點(diǎn)代碼蝗岖,用于打印出class和category,代碼如下:

我不會(huì)分析這個(gè)方法魄揉,只需要注意我添加了()的那一行*

static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);
    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    while (i--) {
        auto& entry = cats->list[i];
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);

(*)    printf("class name is: %s, category name is: %s\n", cls->mangledName(), (*(entry.cat)).name);
        
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);
    rw->properties.attachLists(proplists, propcount);
    free(proplists);
    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}

運(yùn)行之前剪侮,我預(yù)期上面寫的4個(gè)分類都可以打印出來(lái),格式應(yīng)該是這樣的:

預(yù)期結(jié)果洛退,這4行應(yīng)該穿插在結(jié)果當(dāng)中:
...
class name is: NSObject, category name is: NSObjAddition
...
class name is: NSViewController, category name is: NSVCAddition
...
class name is: ZNObject, category name is: MyObjAddition
...
class name is: ZNViewController, category name is: MyVCAddition
...

結(jié)果當(dāng)然不是這樣的瓣俯,不然我也不會(huì)寫這篇文章了:

NSObjAddition.png
NSVCAddition.png

前2個(gè)分類都順利找到了,但是后兩個(gè)分類卻搜索不到兵怯。

為了更清晰一點(diǎn)彩匕,我把那行printf修改了一下,這樣可以過(guò)濾掉無(wú)關(guān)的信息:

if (!strcmp((*(entry.cat)).name, "NSObjAddition")
    || !strcmp((*(entry.cat)).name, "MyObjAddition")
    || !strcmp((*(entry.cat)).name, "NSVCAddition")
    || !strcmp((*(entry.cat)).name, "MyVCAddition")) {
    printf("class name is: %s, category name is: %s\n", cls->mangledName(), (*(entry.cat)).name);
}

結(jié)果當(dāng)然還是一樣的:

屏幕快照 2017-02-24 上午11.54.02.png

一共只有兩行媒区,而不是預(yù)期的四行驼仪。

我一度以為是在類首次被調(diào)用時(shí),通過(guò)realizeClass()方法調(diào)用methodizeClass()再調(diào)用attachCategories()進(jìn)行的袜漩,然而即使我主動(dòng)調(diào)用了category中的方法绪爸,還是不會(huì)有信息打印出來(lái)。

所以問(wèn)題就來(lái)了宙攻,上圖中2和3兩塊的category究竟是什么時(shí)候被附加到類上的呢奠货?

猜測(cè):既然在運(yùn)行期找不到附加的過(guò)程,那是不是編譯期就已經(jīng)做完這個(gè)操作了呢座掘?

為了驗(yàn)證這個(gè)猜測(cè)递惋,我在之前創(chuàng)建的ZNObject+MyObjAddition分類中添加了一個(gè)方法:

// ZNObject+MyObjAddition.h
#import "ZNObject.h"
@interface ZNObject (MyObjAddition)
- (void)printMyObjAddition;
@end

// ZNObject+MyObjAddition.m
#import "ZNObject+MyObjAddition.h"
@implementation ZNObject (MyObjAddition)
- (void)printMyObjAddition {
    NSLog(@"MyObjAddition");
}
@end

接著在_objc_init()方法的最開(kāi)始添加了一個(gè)斷點(diǎn)柔滔,然后獲取了一下ZNObject類的方法列表:

ZNObject分類方法.png

可以看到分類的方法這個(gè)時(shí)候已經(jīng)在baseMethodList中了,所以說(shuō)在編譯期結(jié)束的時(shí)候分類就已經(jīng)附加完成了萍虽。

我又對(duì)NSObject做了相同的測(cè)試睛廊,結(jié)果是NSObject+NSObjAddition分類的方法在這個(gè)時(shí)候并沒(méi)有被添加進(jìn)來(lái)。確實(shí)就是在attachCategories()方法中被添加的杉编。

所以才有了文章最開(kāi)始我放出的那個(gè)結(jié)論超全,這里再列一下作為本文的結(jié)尾吧:

  • 對(duì)系統(tǒng)提供的類做分類,分類是在運(yùn)行時(shí)附加到相關(guān)類上的
  • 對(duì)自己創(chuàng)建的類做分類王财,分類是在編譯期附加到相關(guān)類上的

這就是我測(cè)試下來(lái)的結(jié)論卵迂,這個(gè)結(jié)論看起來(lái)實(shí)在是太奇怪了。也完全找不到相關(guān)文章佐證绒净,所以见咒。。挂疆。

如果有同學(xué)對(duì)此有更深入的研究改览,請(qǐng)一定留言,不甚感激缤言!

ps:這個(gè)過(guò)程也告訴我一個(gè)信息宝当,即使幾乎所有人都那么說(shuō),結(jié)果也可能是錯(cuò)誤的胆萧,紙上得來(lái)終覺(jué)淺呀~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庆揩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子跌穗,更是在濱河造成了極大的恐慌订晌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚌吸,死亡現(xiàn)場(chǎng)離奇詭異锈拨,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)羹唠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門奕枢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人佩微,你說(shuō)我怎么就攤上這事缝彬。” “怎么了哺眯?”我有些...
    開(kāi)封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵跌造,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)壳贪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任寝杖,我火速辦了婚禮违施,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瑟幕。我一直安慰自己磕蒲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布只盹。 她就那樣靜靜地躺著辣往,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殖卑。 梳的紋絲不亂的頭發(fā)上站削,一...
    開(kāi)封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音孵稽,去河邊找鬼许起。 笑死,一個(gè)胖子當(dāng)著我的面吹牛菩鲜,可吹牛的內(nèi)容都是我干的园细。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼接校,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼猛频!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蛛勉,我...
    開(kāi)封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鹿寻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后董习,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體烈和,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年皿淋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了招刹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡窝趣,死狀恐怖疯暑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哑舒,我是刑警寧澤妇拯,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響越锈,放射性物質(zhì)發(fā)生泄漏仗嗦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一甘凭、第九天 我趴在偏房一處隱蔽的房頂上張望稀拐。 院中可真熱鬧,春花似錦丹弱、人聲如沸德撬。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜓洪。三九已至,卻和暖如春坯苹,著一層夾襖步出監(jiān)牢的瞬間隆檀,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工北滥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刚操,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓再芋,卻偏偏與公主長(zhǎng)得像菊霜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子济赎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉鉴逞,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,681評(píng)論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,544評(píng)論 33 466
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)司训,斷路器构捡,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • 1.項(xiàng)目經(jīng)驗(yàn) 2.基礎(chǔ)問(wèn)題 3.指南認(rèn)識(shí) 4.解決思路 ios開(kāi)發(fā)三大塊: 1.Oc基礎(chǔ) 2.CocoaTouch...
    陽(yáng)光的大男孩兒閱讀 4,969評(píng)論 0 13
  • 參考文章 清晰理解Objective-C元類object_getClass(obj)與[obj class]的區(qū)別...
    黑化肥發(fā)灰閱讀 1,025評(píng)論 0 51