如何判断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