在OC中:

API: class_addMethod往一個Class里添加method

API: class_getInstanceMethod或class_getClassMethod可以判斷某個SEL是否存在于Class

API: method_exchangeImplementations 交換方法。

 

最近工作上做了一件事,簡單點說就是需要把一些特定Class里的方法func,替換成Hook_func,當(dāng)Hook_func執(zhí)行完之后,再執(zhí)行func。于是很簡單地想到了往Class添加一個Hook_func,然后再交換func與Hook_func,就能到達目的。

 

但是,在實現(xiàn)后,卻出現(xiàn)了死循環(huán),糾其原因,因為部分Class間存在著繼承關(guān)系,沒有正確地將Method添加到正確的Class中導(dǎo)致。

 

當(dāng)時為了解決這個問題,重新去理清楚了一下Class中 SEL 與 Method的關(guān)系。SEL是一個選擇器,相當(dāng)于指向一個Method的指針,將SEL指向不同的Method,它就會有不一樣的特性,method_exchangeImplementations也就是交換SEL指向的Method值來實現(xiàn)方法交換的。

 

在調(diào)用class_getInstanceMethod時,是會檢查superClass的。

 

如下圖,當(dāng)ClassB繼承ClassA時, ClassB中沒有SEL func,而ClassA中有SEL func。

 

 平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

 

這時調(diào)用class_getInstanceMethod(ClassB, @selector(func)),是能拿到SEL func的Method的,然后再將ClassB中添加SEL Hook_func后,變成下圖。

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

 

對ClassB調(diào)用method_exchangeImplementations后,得到下圖。

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

 

這里可以看到,其實ClassA中的SEL:func已經(jīng)是指向Method:Hook_func。在對ClassB的實例調(diào)用SEL:func時,能達到之前預(yù)訂的效果,即先執(zhí)行Hook_func后再執(zhí)行func。

但在這個時候,如果再對ClassA做類似ClassB的處理,將得到下圖:

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

這時SEL:func和SEL:Hook_func都指向了Method:Hook_func,于是便出現(xiàn)了死循環(huán)的問題。

 

最終的解決方法從上圖已經(jīng)可以很明顯的看出來了,即在對ClassB做處理的時候,添加的SEL:Hook_func不應(yīng)該添加到ClassB上,而應(yīng)該添加到ClassA上,如下圖,則問題得已解決。

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

 

執(zhí)行下面的這個函數(shù),找到正確的Class,然后再往Class里添加方法和交換方法,問題就解決了:

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)

 Class GetSelectorInsClass(Class hclass, SEL sel, Method sel_method) {
    Class super_class = [hclass superclass];    if (!super_class) {        return hclass;
    }
    Method method = class_getInstanceMethod(super_class, sel);    if (method != sel_method) {        return hclass;
    }    return GetSelectorInsClass(super_class, sel, sel_method);
}

平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),游戲開發(fā),動畫培訓(xùn)