如何判斷method是否被swizzling

背景

Objective-C的動態性可讓咱們幹不少事情。好比method swizzling.
method swizzling有不少負面影響,特別是引入第三方組件後. 若是但願在運行時檢測,method是否被swizzling呢?html

最近在翻看clang文檔時發現可使用這樣一種方法:segmentfault

代碼

static inline bool isDiff(const char *func, SEL _cmd)
{
    char buff[256] = {'\0'};
    if (strlen(func) > 2) {
        char* s = strstr(func, " ") + 1;
        char* e = strstr(func, "]");
        memcpy(buff, s, sizeof(char) * (e - s) );
        return strcmp(buff, sel_getName(_cmd));
    }
    return false;
}

#define ALERT_IF_METHOD_REPLACED assert(!isDiff(__PRETTY_FUNCTION__, _cmd));

解釋

__PRETTY_FUNCTION__是一個編譯器的宏,在編譯期就由編譯器決定了,以-[MyObject dosth]爲例,在該方法內__PRETTY_FUNCTION__爲"-[MyObject dosth]".而_cmd是方法的隱藏參數,是動態的,一樣以-[MyObject dosth]爲例,_cmd爲"dosth",若是咱們執行了一下代碼:函數

Method ori_method = class_getInstanceMethod([MyObject class], @selector(dosth));
Method replace_method = class_getInstanceMethod([MyObject class], @selector(dosth2));
method_exchangeImplementations(ori_method, replace_method);

那麼在原來的-[MyObject dosth]函數體中,_cmd爲"dosth2",而__PRETTY_FUNCTION__保持不變。ui

利用上述特性,前文提到的宏就能夠工做了。以下述代碼:code

Method ori_method = class_getInstanceMethod([MyObject class], @selector(dosth));
Method replace_method = class_getInstanceMethod([MyObject class], @selector(dosth2));
method_exchangeImplementations(ori_method, replace_method);

- (void) dosth
{
    ALERT_IF_METHOD_REPLACED;
}

- (void) dosth2
{
    [self dosth2];
}

總結

然而通常狀況下此方法,並無什麼卵用╮(╯_╰)╭ 
由於但凡重要的方法,通常是系統方法,如-[UIViewController viewDidLoad],由於沒法在該函數體使用上述宏,因此沒法檢測是否有swizzle。此外若是在dosth2中並無調用dosth2,那麼也沒有效果。htm

但話分兩頭,在一些大型項目中,使用此方法可用於檢測關鍵方法是否被二方庫三方庫Hook。另外,爲了防止App中關鍵方法被惡意注入,此方法也非常有參考價值的。ip

一些參考

這裏有一篇講如何獲取當前Method的IMP,戳這裏.試了一下,年代比較久遠。可用性已經不太好了。╮(╯_╰)╭ 
不過。在做者的實現使用了__builtin_return_address(0),獲取當前棧的返回地址。這是值得習得的小技能。文檔

後話

後面會繼續找一些優雅的檢測系統方法被method swizzling的情形。IMP相關的東西比較有意思。持續發現中。get

原做寫於segmentfault 連接cmd

相關文章
相關標籤/搜索