动态添加方法的代码分析

动态添加方法的代码分析

之前自己写代码时只是单纯的使用method_exchangeImplementations来进行方法的相互替换,但是偶然间看到别人写的方法替换,发现很不错,很严谨, 代码如下:

+ (void)load{
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        Class class = [self class];
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(skx_viewWillAppear:);
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (success) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

初看方法,第一感觉比较困惑的是为什么在load方法中还要进行dispatch_once. 对此的解释是:虽然load方法只执行一次, 但是说不准某些人会显示调用呢. 所以为了稳妥, 还是加个dispatch_once.

method_exchangeImplementations 这个方法就不介绍了,主要作用是交换两个方法的IMP.

主要看这里:

BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

if (success) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}

首先进行的第一步是addMethod. why? 而且还是拿的原始的selector去进行方法的添加. 但是我们所需要的不正是对原始selector对应的IMP的替换吗? 为什么会有success这个判断呢?

答案只有一个, 那就是此类中其实不存在此方法.这个方法是从父类继承而来的.这时就需要先判断一下:如果方法实际是从父类继承而来的, 那么addMethod就会返回true, 进而再去进行方法的替换.

在此方法是从父类继承而来的情况下, 方法的method_exchangeImplementations还是可以成功的,子类正常使用还是没问题的. 但是如果父类调用了这个被替换的方法, 那么就会崩溃. 因为方法的添加替换都是在子类中进行的, 父类找不到这个方法.

综上, 别人写的代码还是很严谨啊, 还要多学习.

posted @ 2016-01-20 22:08  iShao  阅读(243)  评论(0编辑  收藏  举报