OC方法交換的優(yōu)雅實(shí)現(xiàn)

前言

剛才翻代碼時(shí)發(fā)現(xiàn)N年前寫的方法交換缅叠,當(dāng)時(shí)方法交換還是個(gè)新奇的東東厉颤,網(wǎng)上找了一番發(fā)現(xiàn)都有各種問(wèn)題,于是動(dòng)手寫了一個(gè)渤滞。如今方法交換的寫法已經(jīng)爛大街了,但把我當(dāng)時(shí)寫的拿出來(lái)一對(duì)比榴嗅,不得不自吹一句妄呕,還是我自己寫的更優(yōu)雅。

上代碼

我當(dāng)年的實(shí)現(xiàn)代碼

static void exchangeSelector(Class oClass, SEL oSelector, Class sClass, SEL sSelector) {
    
    Method originalMethod = class_getInstanceMethod(oClass, oSelector);
    Method swizzledMethod = class_getInstanceMethod(sClass, sSelector);
    
    IMP oIMP = method_getImplementation(originalMethod);
    IMP sIMP = method_getImplementation(swizzledMethod);
    
    const char *oType = method_getTypeEncoding(originalMethod);
    const char *sType = method_getTypeEncoding(swizzledMethod);
    
    class_replaceMethod(oClass, oSelector, sIMP, sType);
    class_replaceMethod(oClass, sSelector, oIMP, oType);
    
}

與某流行的代碼對(duì)比

放在一起看嗽测,單論顏值就不是一個(gè)檔次的

16274538549564.jpg

分析

目的

我們使用方法交換的目的實(shí)際是修改方法绪励,當(dāng)然還要保持原方法的實(shí)現(xiàn)可被利用肿孵。
如圖:新方法的實(shí)現(xiàn)和原方法交換后,通過(guò)新方法名就可以使用原本的實(shí)現(xiàn)疏魏。

16273935303846.jpg

問(wèn)題

而出現(xiàn)的BUG停做,通常是原方法的實(shí)現(xiàn)并不在類本身,而是在父類中大莫,替換的時(shí)候影響到了父類

16273940647544.jpg

我們想要的結(jié)果

16274438965756.jpg

處理過(guò)程

理論處理

大象裝冰箱分幾步蛉腌?1.獲取大象,2.放進(jìn)冰箱只厘。
我們?nèi)绾谓粨Q變量呢烙丛?1.獲取舊值,2.放進(jìn)新變量羔味。下面的寫法最容易理解了吧河咽。

{
    //交換a, b兩個(gè)變量的值;
    tmp1 = a;
    tmp2 = b;
    a = tmp2;
    b = tmp1;
}

接下來(lái)是交換方法,我們?nèi)绾谓粨Q方法呢介评?1.獲取舊實(shí)現(xiàn)库北,2.放進(jìn)新方法爬舰。
把class比喻成字典们陆,把方法名比喻成key,把方法實(shí)現(xiàn)比喻成value情屹,把class有父類比喻字典也像NSUserDefaults一樣有更深的域坪仇,那么我們要做的事情就是這個(gè)

exchangeSelector(funA, funB) {
    class = self.class;
    methodA = class[funA];
    methodB = class[funB];
    class[funA] = methodB;
    class[funB] = methodA;
}

再?gòu)?fù)雜點(diǎn),methodB 沒(méi)必要限定在本類垃你,畢竟要加一堆類別也挺煩的椅文,而且有些類是隱藏的,強(qiáng)行聲明出來(lái)再加類別擴(kuò)展方法總感覺(jué)很不靠譜惜颇。于是我們指定 methodB 皆刺,由于methodB的獲取不是常用方法,我們換成把獲取 methodB 所需的條件傳入

exchangeSelector(class, funA, classForMethodB, funForMethodB) {
    methodA = class[funA];
    methodB = classForMethodB[funForMethodB];
    class[funA] = methodB;
    class[funB] = methodA;
}

我當(dāng)年的處理

就是按上面最單純的過(guò)程凌摄,換成最直白的代碼羡蛾。

  1. 獲取實(shí)現(xiàn)
16273944303240.jpg
    //原實(shí)現(xiàn)
    Method originalMethod = class_getInstanceMethod(oClass, oSelector);
    IMP oIMP = method_getImplementation(originalMethod);
    const char *oType = method_getTypeEncoding(originalMethod);
    
    //新實(shí)現(xiàn)
    Method swizzledMethod = class_getInstanceMethod(sClass, sSelector);
    IMP sIMP = method_getImplementation(swizzledMethod);
    const char *sType = method_getTypeEncoding(swizzledMethod);
  1. 把實(shí)現(xiàn)放進(jìn)方法。
16273945396107.jpg
    //把新實(shí)現(xiàn)放進(jìn)原方法
    class_replaceMethod(oClass, oSelector, sIMP, sType);
    //把原實(shí)現(xiàn)放進(jìn)新方法锨亏。這里要注意痴怨,我們的目的只是替換原本類里的方法。
    class_replaceMethod(oClass, sSelector, oIMP, oType);

什么器予?你問(wèn)上面那個(gè)BUG的問(wèn)題浪藻,原方法子類里沒(méi)實(shí)現(xiàn),是在父類實(shí)現(xiàn)的怎么辦乾翔?

如果讓你給一個(gè)變量賦值爱葵,你是這樣做

int a;  //假如有一個(gè)變量a,我們要給它賦值
if (a) {    //先判斷a原本是否有值
    a = 1;  //如果a已經(jīng)有值了,就將a的舊值改成新值
} else {
    a = 1;  //如果a沒(méi)值,則直接使用新值
}

還是這樣做

int a;  //假如有一個(gè)變量a,我們要給它賦值
a = 1;  //不判斷a當(dāng)前是否有值萌丈,直接賦值

我選擇后者暇韧。

如果你來(lái)設(shè)計(jì)一個(gè)函數(shù),用來(lái)設(shè)置字典中某個(gè)key所對(duì)應(yīng)的值浓瞪,你會(huì)設(shè)計(jì)成當(dāng)別人使用時(shí)懈玻,需要像左邊這樣先判斷當(dāng)前是否有值,再根據(jù)不同的情況進(jìn)行不同的處理乾颁,還是向右邊這樣一步搞定涂乌?

16273964339426.jpg

我還是會(huì)選擇后者,我問(wèn)過(guò)別人英岭,非常巧合的是他們也選擇后者湾盒,更巧合的是 class_replaceMethod 也和我們一樣選擇了后者。所以我們根本不用考慮子類里有沒(méi)有實(shí)現(xiàn)這種事情诅妹。

某流行處理

不多說(shuō)了罚勾,除了 class_replaceMethod 還要了解 class_addMethod、method_exchangeImplementations 吭狡,然后再做判斷尖殃,不同分支做不同處理,看起來(lái)就容易令人迷惑的樣子划煮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末送丰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子弛秋,更是在濱河造成了極大的恐慌器躏,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟹略,死亡現(xiàn)場(chǎng)離奇詭異登失,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)挖炬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門揽浙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人茅茂,你說(shuō)我怎么就攤上這事捏萍。” “怎么了空闲?”我有些...
    開(kāi)封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵令杈,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碴倾,道長(zhǎng)逗噩,這世上最難降的妖魔是什么掉丽? 我笑而不...
    開(kāi)封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮异雁,結(jié)果婚禮上捶障,老公的妹妹穿的比我還像新娘。我一直安慰自己纲刀,他們只是感情好项炼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著示绊,像睡著了一般锭部。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上面褐,一...
    開(kāi)封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天拌禾,我揣著相機(jī)與錄音,去河邊找鬼展哭。 笑死湃窍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匪傍。 我是一名探鬼主播您市,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼析恢!你這毒婦竟也來(lái)了墨坚?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤映挂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后盗尸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體柑船,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年泼各,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鞍时。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扣蜻,死狀恐怖逆巍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莽使,我是刑警寧澤锐极,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站芳肌,受9級(jí)特大地震影響灵再,放射性物質(zhì)發(fā)生泄漏肋层。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一翎迁、第九天 我趴在偏房一處隱蔽的房頂上張望栋猖。 院中可真熱鬧,春花似錦汪榔、人聲如沸蒲拉。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)全陨。三九已至,卻和暖如春衷掷,著一層夾襖步出監(jiān)牢的瞬間辱姨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工戚嗅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留雨涛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓懦胞,卻偏偏與公主長(zhǎng)得像替久,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躏尉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

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