iOS黑魔法-Method Swizzling

需求: 本身項(xiàng)目已經(jīng)很大,要新增頁面訪問統(tǒng)計(jì)功能。

Method Swizzling原理:Method Swizzling是發(fā)生在運(yùn)行時(shí)的,主要是將兩個(gè)method進(jìn)行交換,我們可以將Method Swizzling代碼寫到任何地方,但是只有這段Method Swizzing代碼執(zhí)行完畢之后互換才起作用。

Method Swizzling使用:在實(shí)現(xiàn)Method Swizzling時(shí),核心代碼主要就是一個(gè)runtime的C語言API:

#import "UIViewController+swizzling.h"
#import <objc/runtime.h>
@implementation UIViewController (swizzling)

+ (void)load {
    // 通過class_getInstanceMethod()函數(shù)從當(dāng)前對(duì)象中的method list獲取method結(jié)構(gòu)體,如果是類方法就使用class_getClassMethod()函數(shù)獲取。
    Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));
    Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewDidLoad));
    /**
     *  我們?cè)谶@里使用class_addMethod()函數(shù)對(duì)Method Swizzling做了一層驗(yàn)證,如果self沒有實(shí)現(xiàn)被交換的方法,會(huì)導(dǎo)致失敗。
     *  而且self沒有交換的方法實(shí)現(xiàn),但是父類有這個(gè)方法,這樣就會(huì)調(diào)用父類的方法,結(jié)果就不是我們想要的結(jié)果了。
     *  所以我們?cè)谶@里通過class_addMethod()的驗(yàn)證,如果self實(shí)現(xiàn)了這個(gè)方法,class_addMethod()函數(shù)將會(huì)返回NO,我們就可以對(duì)其進(jìn)行交換了。
     */
    if (!class_addMethod([self class], @selector(swizzlingViewDidLoad), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        method_exchangeImplementations(fromMethod, toMethod);
    }
}

// 我們自己實(shí)現(xiàn)的方法,也就是和self的viewDidLoad方法進(jìn)行交換的方法。
- (void)swizzlingViewDidLoad {
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我們?cè)谶@里加一個(gè)判斷,將系統(tǒng)的UIViewController的對(duì)象剔除掉
    if(![str containsString:@"UI"]){
        NSLog(@"統(tǒng)計(jì)打點(diǎn) : %@", self.class);
    }
    [self swizzlingViewDidLoad];
}
@end

Method Swizzling類簇:項(xiàng)目開發(fā)過程中,經(jīng)常因?yàn)镹SArray數(shù)組越界或者NSDictionary的key或者value值為nil等問題導(dǎo)致的崩潰,對(duì)于這些問題蘋果并不會(huì)報(bào)一個(gè)警告,而是直接崩潰,感覺蘋果這樣確實(shí)有點(diǎn)“太狠了”。

由此,我們可以根據(jù)上面所學(xué),對(duì)NSArray、NSMutableArray、NSDictionary、NSMutableDictionary等類進(jìn)行Method Swizzling,實(shí)現(xiàn)方式還是按照上面的例子來做。但是....你發(fā)現(xiàn)Method Swizzling根本就不起作用,代碼也沒寫錯(cuò)啊,到底是什么鬼?

這是因?yàn)镸ethod Swizzling對(duì)NSArray這些的類簇是不起作用的。因?yàn)檫@些類簇類,其實(shí)是一種抽象工廠的設(shè)計(jì)模式。抽象工廠內(nèi)部有很多其它繼承自當(dāng)前類的子類,抽象工廠類會(huì)根據(jù)不同情況,創(chuàng)建不同的抽象對(duì)象來進(jìn)行使用。例如我們調(diào)用NSArray的objectAtIndex:方法,這個(gè)類會(huì)在方法內(nèi)部判斷,內(nèi)部創(chuàng)建不同抽象類進(jìn)行操作。

所以也就是我們對(duì)NSArray類進(jìn)行操作其實(shí)只是對(duì)父類進(jìn)行了操作,在NSArray內(nèi)部會(huì)創(chuàng)建其他子類來執(zhí)行操作,真正執(zhí)行操作的并不是NSArray自身,所以我們應(yīng)該對(duì)其“真身”進(jìn)行操作。

#import "NSArray+LXZArray.h"
#import "objc/runtime.h"
@implementation NSArray (LXZArray)
+ (void)load {
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(lxz_objectAtIndex:));
    method_exchangeImplementations(fromMethod, toMethod);
}

- (id)lxz_objectAtIndex:(NSUInteger)index {
    if (self.count-1 < index) {
        // 這里做一下異常處理,不然都不知道出錯(cuò)了。
        @try {
            return [self lxz_objectAtIndex:index];
        }
        @catch (NSException *exception) {
            // 在崩潰后會(huì)打印崩潰信息,方便我們調(diào)試。
            NSLog(@"---------- %s Crash Because Method %s  ----------\n", class_getName(self.class), __func__);
            NSLog(@"%@", [exception callStackSymbols]);
            return nil;
    }
        @finally {}
    } else {
        return [self lxz_objectAtIndex:index];
    }
}
@end

__NSArrayI才是NSArray真正的類,而NSMutableArray又不一樣。我們可以通過runtime函數(shù)獲取真正的類:

objc_getClass("__NSArrayI")
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 該文章屬于劉小壯原創(chuàng),轉(zhuǎn)載請(qǐng)注明:劉小壯[http://www.lxweimin.com/u/2de707c93d...
    劉小壯閱讀 43,571評(píng)論 141 272
  • 前言: 今天我們?cè)賮砹私饬硗庖粋€(gè)體現(xiàn)OC動(dòng)態(tài)特性的技術(shù),向來有IOS黑魔法之稱的Method Swizzling,...
    cxlhaha閱讀 732評(píng)論 0 3
  • 剛開始學(xué)習(xí)IOS的時(shí)候,聽說黑魔法很強(qiáng)大,正如它的名字一樣,可以做很多不可思議的事情,一直到今天才徹底靜下心去了解...
    東了個(gè)尼閱讀 1,734評(píng)論 0 3
  • iOS的runtime中有一種神奇的黑魔法: Method Swizzling,利用它可以做很多有趣的事情。 Me...
    焚雪殘陽閱讀 3,553評(píng)論 7 16
  • 場(chǎng)景需求:在沒有一個(gè)類的實(shí)現(xiàn)源碼的情況下,想改變其中一個(gè)方法(一般指系統(tǒng)的方法)的實(shí)現(xiàn),除了繼承它重寫、和借助類別...
    船長(zhǎng)_閱讀 2,138評(píng)論 0 17