iOS 黑魔法 Method Swizzing 的一個坑

項目中針對使用NSArray的objectAtIndex:函數(shù)時第步,數(shù)組越界問題尾菇,可以用category配合runtime的黑魔法 Method Swizzing解決。

#import "NSArray+Checking.h"
#import <objc/runtime.h>

@implementation NSArray (Checking)

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        Method objAtIndex = class_getInstanceMethod(class, @selector(objectAtIndex:));
        Method objAtIndexCheck = class_getInstanceMethod(class, @selector(objAtIndexCheck:));
        
        BOOL didAddMethod = class_addMethod([self class], @selector(objAtIndexCheck:), method_getImplementation(objAtIndexCheck), method_getTypeEncoding(objAtIndexCheck));
        
        if (didAddMethod) {
            class_replaceMethod([self class], @selector(objAtIndexCheck:), method_getImplementation(objAtIndex), method_getTypeEncoding(objAtIndex));
        } else {
            method_exchangeImplementations(objAtIndex, objAtIndexCheck);
        }
    });
}

- (id)objAtIndexCheck:(NSInteger)index{
    if (self.count <= index) {
        return nil;
    }
    return [self objAtIndexCheck:index];
}

@end

這樣當(dāng)調(diào)用【NSArray objectAtIndex:】時橄浓,就會先先調(diào)用objAtIndexCheck:份氧,然后做一些自己的判斷,再去調(diào)用系統(tǒng)的函數(shù)诗茎。

但是問題來了
上面的代碼函數(shù)objAtIndexCheck:并沒有被成功調(diào)用工坊,檢查了一下午,無數(shù)次的驗證敢订,仍然沒有結(jié)果王污。
我懷疑寫法問題?還是這種被廣大程序猿吹噓的方法根本就是錯的楚午?
然后我進行了嘗試

//在一個類里面這樣寫玉掸。
    Method objAtIndex = class_getInstanceMethod(self.class, @selector(aa));
    Method objAtIndexCheck = class_getInstanceMethod(self.class, @selector(bb));
    
    method_exchangeImplementations(objAtIndex, objAtIndexCheck);

    [self aa];
    [self bb];

完全沒有問題。

我問了朋友醒叁,朋友給了我一篇文章
作者遇到了和我一樣的問題司浪,他進行了如下嘗試,也沒有問題把沼。(我沒測試)

+(void)load{ 
  static dispatch_once_t onceToken ; 
  dispatch_once(&onceToken, ^{ 
    Method m1 = class_getInstanceMethod([self class], @selector(lastObject)); 
    Method m2 = class_getInstanceMethod([self class], @selector(My_lastObject)); 
    BOOL isAdd = class_addMethod([self class], @selector(lastObject), method_getImplementation(m2), method_getTypeEncoding(m2)); 
    if (isAdd == YES) { 
        class_replaceMethod([self class], @selector(My_lastObject), method_getImplementation(m1), method_getTypeEncoding(m1)); 
    }else{ 
        method_exchangeImplementations(m1, m2); 
} 
    Method m3 = class_getInstanceMethod([self class], @selector(firstObject)); 
    Method m4 = class_getInstanceMethod([self class], @selector(My_fistObjcet)); 
    method_exchangeImplementations(m3, m4); 
});
}

-(id)My_fistObjcet{ 
  NSLog(@"My_fistObjcet : exchangeSuccess"); 
  return [self My_fistObjcet];
}

-(id)My_lastObject{ 
  NSLog(@"My_lastObject : exchangeSuccess"); 
  return [self My_lastObject];
}

-------結(jié)論------
并不是不支持啊易,而是使用錯了類名,對于class_getInstanceMethod函數(shù)第一個參數(shù)的class饮睬,使用的不是[self class]租谈,而應(yīng)該是需要的類alloc init之后的真實類,NSArray對應(yīng)__NSArrayI捆愁,NSMutableArray對應(yīng)__NSArrayM割去,而后面的函數(shù)class_addMethod、class_replaceMethod使用的則是[self class]昼丑。進行如下修改就沒問題了

Class class = NSClassFromString(@"__NSArrayI");
Method objAtIndex = class_getInstanceMethod(class, @selector(objectAtIndex:));
Method objAtIndexCheck = class_getInstanceMethod(class, @selector(objAtIndexCheck:));
        
BOOL didAddMethod = class_addMethod([self class], @selector(objAtIndexCheck:), method_getImplementation(objAtIndexCheck), method_getTypeEncoding(objAtIndexCheck));
        
if (didAddMethod) {
    class_replaceMethod([self class], @selector(objAtIndexCheck:), method_getImplementation(objAtIndex), method_getTypeEncoding(objAtIndex));
} else {
    method_exchangeImplementations(objAtIndex, objAtIndexCheck);
}

NSArray Method Swizzing.png

對于OC數(shù)組來說呻逆,NSArray確實是基類,[NSArray class]是NSArray菩帝,但是NSArray *array = [[NSArray alloc] init];之后咖城,這時的array的類就不是NSArray了而是__NSArrayI茬腿。具體為什么不是很清楚,應(yīng)該是OC的類簇的問題宜雀。

所以先找到實例的具體類切平,然后再確定參數(shù)的class

NSString類.png
/** 
 * Returns a specified instance method for a given class.
 * 
 * @param cls The class you want to inspect.
 * @param name The selector of the method you want to retrieve.
 * 
 * @return The method that corresponds to the implementation of the selector specified by 
 *  \e name for the class specified by \e cls, or \c NULL if the specified class or its 
 *  superclasses do not contain an instance method with the specified selector.
 *
 * @note This function searches superclasses for implementations, whereas \c class_copyMethodList does not.
 */
OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);



/** 
 * Adds a new method to a class with a given name and implementation.
 * 
 * @param cls The class to which to add a method.
 * @param name A selector that specifies the name of the method being added.
 * @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
 * @param types An array of characters that describe the types of the arguments to the method. 
 * 
 * @return YES if the method was added successfully, otherwise NO 
 *  (for example, the class already contains a method implementation with that name).
 *
 * @note class_addMethod will add an override of a superclass's implementation, 
 *  but will not replace an existing implementation in this class. 
 *  To change an existing implementation, use method_setImplementation.
 */
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, 
                                 const char *types) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

最后同樣測試了一下NSString,同樣的情況辐董。

#import "NSString+Check.h"
#import <objc/runtime.h>

@implementation NSString (Check)

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = NSClassFromString(@"__NSCFConstantString");
        Method objAtIndex = class_getInstanceMethod(class, @selector(isEqualToString:));
        Method objAtIndexCheck = class_getInstanceMethod(class, @selector(isEqualToStringCheck:));
        
        BOOL didAddMethod = class_addMethod([self class], @selector(isEqualToStringCheck:), method_getImplementation(objAtIndexCheck), method_getTypeEncoding(objAtIndexCheck));
        
        if (didAddMethod) {
            class_replaceMethod([self class], @selector(isEqualToStringCheck:), method_getImplementation(objAtIndex), method_getTypeEncoding(objAtIndex));
        } else {
            method_exchangeImplementations(objAtIndex, objAtIndexCheck);
        }
    });
}

- (BOOL)isEqualToStringCheck:(NSString *)aString{
    NSLog(@"aaa = %@",aString);
    return [self isEqualToStringCheck:aString];
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悴品,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子简烘,更是在濱河造成了極大的恐慌苔严,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夸研,死亡現(xiàn)場離奇詭異,居然都是意外死亡依鸥,警方通過查閱死者的電腦和手機亥至,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贱迟,“玉大人姐扮,你說我怎么就攤上這事∫路停” “怎么了茶敏?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缚俏。 經(jīng)常有香客問我惊搏,道長,這世上最難降的妖魔是什么忧换? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任恬惯,我火速辦了婚禮,結(jié)果婚禮上亚茬,老公的妹妹穿的比我還像新娘酪耳。我一直安慰自己,他們只是感情好刹缝,可當(dāng)我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布碗暗。 她就那樣靜靜地躺著,像睡著了一般梢夯。 火紅的嫁衣襯著肌膚如雪言疗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天颂砸,我揣著相機與錄音洲守,去河邊找鬼疑务。 笑死,一個胖子當(dāng)著我的面吹牛梗醇,可吹牛的內(nèi)容都是我干的知允。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼叙谨,長吁一口氣:“原來是場噩夢啊……” “哼温鸽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起手负,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤涤垫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竟终,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝠猬,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年统捶,在試婚紗的時候發(fā)現(xiàn)自己被綠了榆芦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡喘鸟,死狀恐怖匆绣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情什黑,我是刑警寧澤崎淳,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站愕把,受9級特大地震影響拣凹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恨豁,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一咐鹤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧圣絮,春花似錦祈惶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棒搜,卻和暖如春疹蛉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背力麸。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工可款, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留育韩,地道東北人。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓闺鲸,卻偏偏與公主長得像筋讨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子摸恍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,930評論 2 361

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉悉罕,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,734評論 0 9
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,168評論 30 470
  • 景甜《大唐榮耀》劇情介紹,由歡瑞世紀(jì)出品立镶,和力辰光壁袄、千乘影視聯(lián)合出品,鐘聰海擔(dān)任制片人媚媒,劉國楠嗜逻、尹濤聯(lián)合執(zhí)導(dǎo),劉芳...
    雷霆四少老三閱讀 1,357評論 0 0
  • 這招玩的真好缭召,麻蛋
    車尾靠窗閱讀 183評論 0 0
  • 我這個年齡栈顷,27歲,有可能對于我們這輩人不算什么恼琼,但是對于上一輩尤其是女孩這個年齡已經(jīng)大到讓父母擔(dān)心我們會成為剩女...
    zz雪如塵閱讀 284評論 0 0