Runtime 交叉方法父子類細(xì)節(jié)

上一章Runtime 更多應(yīng)用中開頭就談到了Runtime交叉方法的簡(jiǎn)單使用
這里,來深入討論一些細(xì)節(jié),比如子類的方法替換會(huì)不會(huì)影響父類的方法

創(chuàng)建兩個(gè)類 Vampire、June繼承自Vampire
Vampire中寫一個(gè)方法 -Vampire
June中重寫-Vampire,并新增-JuneSwizzle用于交叉方法替換-Vampire的實(shí)現(xiàn)
June是此文的 目標(biāo)類

  • 注意:其實(shí)如果將方法交換寫的嚴(yán)謹(jǐn)一些,需要三個(gè)運(yùn)行時(shí)函數(shù)

  • class_addMethod

  • class_replaceMethod

  • method_exchangeImplementations(上一章只用了這個(gè))

    1. 創(chuàng)建 Vampire
@interface Vampire : NSObject
- (void)Vampire;
@end

@implementation Vampire
- (void)Vampire
{
    NSLog(@" 1 %@     %s",[self class],__func__);
}
@end
    1. 創(chuàng)建 June 類 繼承自 Vampire
#import "Vampire.h"
@interface June : Vampire
@end

* 引入 runtime.h
#import <objc/runtime.h>

@implementation June

* 此方法來自父類 `Vampire`
- (void)Vampire{
    NSLog(@" 2 %@     %s",[self class],__func__);
}

* 用于替換 `-Vampire` 方法實(shí)現(xiàn)
- (void)JuneSwizzle{
    NSLog(@" 33 %@     %s",[self class],__func__);
}
@end
    1. 交叉方法,寫在June.m
  • dispatch_once 這里表示,保證方法替換只執(zhí)行一次,因?yàn)?+load 可當(dāng)做普通類方法調(diào)用,所以,為了避免不小心手動(dòng)調(diào)用了 +load 而造成我們的方法實(shí)現(xiàn)替換效果失效,一般交叉方法替換系統(tǒng)方法或第三方不可見源代碼的場(chǎng)景,均替換一次方法實(shí)現(xiàn);
  • 另一種看我們的實(shí)際需要,如果希望在某些時(shí)刻兩種方法實(shí)現(xiàn)動(dòng)態(tài)交替?zhèn)€性效果,也可不寫 dispatch_once,通過手動(dòng)調(diào)用 +load 實(shí)現(xiàn)不同個(gè)性效果
+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        SEL oriSEL = @selector(Vampire);
        SEL swiSEL = @selector(JuneSwizzle);
        
        Method oriMet = class_getInstanceMethod(self, oriSEL);
        Method swiMet = class_getInstanceMethod(self, swiSEL);

        BOOL didAddMet = class_addMethod(self,
                                         oriSEL,
                                         method_getImplementation(swiMet),
                                         method_getTypeEncoding(swiMet));
        if (didAddMethod) {
            class_replaceMethod(self,
                                swiSEL,
                                method_getImplementation(oriMet),
                                method_getTypeEncoding(oriMet));
            NSLog(@" Add ");
        } else {
            method_exchangeImplementations(oriMet, swiMet);
            NSLog(@" _exchange ");
        }
    });
}
  • 周全起見,有2種情況要考慮

    • 1> -Vampire 沒有在目標(biāo)類June中實(shí)現(xiàn),而是在父類Vampire中實(shí)現(xiàn)了。
    • 2> -Vampire 已經(jīng)在目標(biāo)類June中重寫實(shí)現(xiàn)
    • 這兩種情況要區(qū)別對(duì)待。
  • 運(yùn)行時(shí)函數(shù) class_addMethod 用來檢查目標(biāo)類June中 是否有 -Vampire 的重寫實(shí)現(xiàn)

  • if (didAddMethod)

    • 添加成功:說明目標(biāo)類June-Vampire 沒有 重寫實(shí)現(xiàn),屬情況1>
      □ 那么在目標(biāo)類June增加的 方法和實(shí)現(xiàn),方法名肯定是 Vampire,但是方法實(shí)現(xiàn)是交換方法-JuneSwizzle的實(shí)現(xiàn)和其TypeEncoding
      □ 然后用class_replaceMethod-JuneSwizzle 的實(shí)現(xiàn) 替換為 -Vampire 的實(shí)現(xiàn)和其TypeEncoding
      □ 這里看起來可能會(huì)有點(diǎn)繞,雖然有點(diǎn)繞,但認(rèn)真思考1分鐘會(huì)發(fā)現(xiàn),交叉方法的實(shí)質(zhì)是,2個(gè)方法名還是原來的方法名,只是他們2個(gè)的方法實(shí)現(xiàn)交換了,所以這里的class_addMethod、class_replaceMethod就是為了交換2個(gè)方法的實(shí)現(xiàn),所以這樣寫,一點(diǎn)也不饒 :)

    • 添加失敗:說明目標(biāo)類June-Vampire 已經(jīng) 重寫實(shí)現(xiàn)
      □ 那么就是 情況2>,可以直接通過 method_exchangeImplementations 來完成 交叉方法

  • 為了更清晰的看到,class_addMethod、class_replaceMethod這兩個(gè)運(yùn)行時(shí)函數(shù)的效果,我們來看一下下面的截圖,關(guān)于情況1>

  • 情況2>就不需要截圖演示了,因?yàn)楦居貌坏侥莾蓚€(gè)運(yùn)行時(shí)函數(shù)

  • 我們實(shí)現(xiàn)情況1>的情形,但不實(shí)現(xiàn)那兩個(gè)運(yùn)行時(shí)函數(shù),看是怎樣的打印結(jié)果(實(shí)現(xiàn)那兩個(gè)運(yùn)行時(shí)函數(shù)的截圖也不需要演示了,肯定是交換方法成功了,肯定是想要的打印結(jié)果 :) )

情況1>.png

對(duì)于情況1>,因?yàn)?class_getInstanceMethod 會(huì)返回父類Vampire的實(shí)現(xiàn),如果直接替換,就會(huì)替換掉父類Vampire的實(shí)現(xiàn),而不是目標(biāo)類June中的實(shí)現(xiàn)。(詳細(xì)的函數(shù)說明在這里)

舉個(gè)具體的例子, 假設(shè)要替換掉-[NSArray description],如果 NSArray 沒有實(shí)現(xiàn)-description (可選的) 那你就會(huì)得到NSObject的方法。如果調(diào)用method_exchangeImplementations, 你就會(huì)把 NSObject 的方法替換成你的代碼。這應(yīng)該不會(huì)是你想要的吧?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容