使用category重寫原方法的坑

好久沒寫blog了吗冤,有時(shí)候?qū)幵付嗷〞r(shí)間去研究新東西问顷,也懶得寫。最近在寫category的時(shí)候發(fā)現(xiàn)了很多有意思的東西冗荸,想在這里跟大家分享一下承璃。有時(shí)候我們在創(chuàng)建一個(gè)類別后,重寫了原類的方法俏竞,這個(gè)并不會(huì)報(bào)錯(cuò)绸硕,但是會(huì)有警告“Category is implementing a method which will also be implemented by its primary class”堂竟,運(yùn)行起來也可以執(zhí)行,但是不會(huì)執(zhí)行原類的方法玻佩,很多人認(rèn)為覆蓋執(zhí)行了出嘹。

其實(shí)不然,category只是添加了一個(gè)新的方法咬崔,在執(zhí)行的過程中税稼,取的是方法列表的第一個(gè)方法就沒有再去執(zhí)行其他相同名稱的方法了。我們看源碼:

for (uint32_t m = 0;
             (scanForCustomRR || scanForCustomAWZ)  &&  m < mlist->count;
             m++)
        {
            SEL sel = method_list_nth(mlist, m)->name;
            if (scanForCustomRR  &&  isRRSelector(sel)) {
                cls->setHasCustomRR();
                scanForCustomRR = false;
            } else if (scanForCustomAWZ  &&  isAWZSelector(sel)) {
                cls->setHasCustomAWZ();
                scanForCustomAWZ = false;
            }
        }
        // Fill method list array
        newLists[newCount++] = mlist;
    .
    .
    .
    // Copy old methods to the method list array
    for (i = 0; i < oldCount; i++) {
        newLists[newCount++] = oldLists[i];
    }

源碼中category新添加的方法是放在newLists前面的垮斯,其他方法在后面郎仆,我們總結(jié)一下:
1)、category的方法沒有“替換掉”原類的方法兜蠕,也就是說如果category和原來類都有methodA扰肌,那么category附加完成之后,類的方法列表里會(huì)有兩個(gè)methodA
2)熊杨、category的方法被放到了新方法列表的前面曙旭,而原來類的方法被放到了新方法列表的后面,這也就是我們平常所說的category的方法會(huì)“覆蓋”掉原來類的同名方法晶府,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的桂躏,它只要一找到對應(yīng)名字的方法,就不會(huì)繼續(xù)查找了川陆,但是后面可能還有一樣名字的方法剂习。

所以原類的方法我們還是可以找到的,一般存在于方法列表最后较沪,我們是可以取出來的

if (currentClass) {
    unsigned int methodCount;
    Method *methodList = class_copyMethodList(currentClass, &methodCount);
    IMP lastImp = NULL;
    SEL lastSel = NULL;
    for (NSInteger i = 0; i < methodCount; i++) {
        Method method = methodList[i];
        NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(method)) 
                                        encoding:NSUTF8StringEncoding];
        if ([@"targetName" isEqualToString:methodName]) {
            lastImp = method_getImplementation(method);
            lastSel = method_getName(method);
        }
    }
    typedef void (*fn)(id,SEL);

    if (lastImp != NULL) {
        fn f = (fn)lastImp;
        f(my,lastSel);
    }
    free(methodList);
}

利用這個(gè)原理鳞绕,我們可以Hook原方法之后,找出原方法再執(zhí)行购对。一般我們通過Swizzle去實(shí)現(xiàn)猾昆,現(xiàn)在呢陶因,又多了一種Hook的方法了骡苞。
這種“覆蓋”原方法的編碼方式蘋果本身并不建議,但是如果我們自己把握好節(jié)奏也是可以的楷扬。我個(gè)人也并不建議這樣編寫解幽,因?yàn)楫?dāng)多人同時(shí)編寫代碼時(shí),如果每個(gè)人都寫了自己的一套category并且覆蓋了同樣的方法烘苹,編譯并不會(huì)報(bào)錯(cuò)躲株,那么到底執(zhí)行誰寫的代碼呢,就會(huì)出現(xiàn)執(zhí)行混亂镣衡。所以并不建議使用類別“覆蓋”原方法霜定。如果想要真正覆蓋原方法档悠,可以使用繼承來實(shí)現(xiàn)。

我們再來看另一個(gè)問題望浩,我現(xiàn)在創(chuàng)建了TestMethod和TestMethod2兩個(gè)category辖所,如圖:

@implementation StrategyDemo (TestMethod)

- (void)printMethod
{
    NSLog(@"printMethod 1");
}

@implementation StrategyDemo (TestMethod2)

- (void)printMethod
{
    NSLog(@"printMethod 2");
}

并同時(shí)添加了printMethod的方法,然后我調(diào)用printMethod后到底是打印categoryA的方法還是打印categoryB的方法呢磨德?還是都會(huì)打印呢缘回?

print@2x.png

我們上面講過,category只是添加了一個(gè)新的方法,并沒有覆蓋原方法典挑,只是在執(zhí)行的時(shí)候是取的methodList里最靠前的執(zhí)行酥宴。所以打印結(jié)果一定是編譯時(shí)methodList最先匹配到方法名的方法。我們也可以手動(dòng)調(diào)整編譯順序您觉,進(jìn)而改變methodList中方法的順序拙寡,最后改變執(zhí)行的方法。


buildPhases@2x.png

上圖是調(diào)整前的順序琳水,當(dāng)我調(diào)整后:


buildPhases2@2x.png

再看打印結(jié)果:


print2@2x.png

調(diào)整后確實(shí)是改變了編譯時(shí)methodlist里方法的順序倒庵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市炫刷,隨后出現(xiàn)的幾起案子擎宝,更是在濱河造成了極大的恐慌,老刑警劉巖浑玛,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绍申,死亡現(xiàn)場離奇詭異,居然都是意外死亡顾彰,警方通過查閱死者的電腦和手機(jī)极阅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涨享,“玉大人筋搏,你說我怎么就攤上這事〔匏恚” “怎么了奔脐?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吁讨。 經(jīng)常有香客問我髓迎,道長,這世上最難降的妖魔是什么建丧? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任排龄,我火速辦了婚禮,結(jié)果婚禮上翎朱,老公的妹妹穿的比我還像新娘橄维。我一直安慰自己尺铣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布争舞。 她就那樣靜靜地躺著迄埃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兑障。 梳的紋絲不亂的頭發(fā)上侄非,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音流译,去河邊找鬼逞怨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛福澡,可吹牛的內(nèi)容都是我干的叠赦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼革砸,長吁一口氣:“原來是場噩夢啊……” “哼除秀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起算利,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤册踩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后效拭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體暂吉,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年缎患,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了慕的。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挤渔,死狀恐怖肮街,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情判导,我是刑警寧澤嫉父,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站骡楼,受9級特大地震影響熔号,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸟整,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望朦蕴。 院中可真熱鬧篮条,春花似錦弟头、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至伴栓,卻和暖如春伦连,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钳垮。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工惑淳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人饺窿。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓歧焦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肚医。 傳聞我的和親對象是個(gè)殘疾皇子绢馍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

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