Runtime 方法交换

Runtime基础使用场景-拦截替换方法(class_addMethod ,class_replaceMethod和method_exchangeImplementations)

  • class_addMethod (添加方法)
  • class_replaceMethod (替换方法)
  • method_exchangeImplementations (交换方法)

交换方式一

+(void)load{
    //获取两个类的方法
    Method originalMethod = class_getClassMethod([UIImage class], @selector(imageNamed:));
    Method swizzledMethod = class_getClassMethod([UIImage class], @selector(ll_imageName:));
    //开始交换方法实现
    // 若已经存在,则添加会失败
    BOOL didAddMethod = class_addMethod(class, originalSelector,
                                        method_getImplementation(swizzledMethod),
                                        method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) { // 添加了新方法的直接替换
        class_replaceMethod(class, swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {    // 未添加新方法的交换
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

交换方式二

+ (void)load {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{  // 保证方法只替换一次
        //要特别注意你替换的方法到底是哪个性质的方法
        // When swizzling a Instance method, use the following:
        //        Class class = [self class];

        // When swizzling a class method, use the following:
        Class class = object_getClass((id)self);

        SEL originalSelector = @selector(systemMethod_PrintLog);
        SEL swizzledSelector = @selector(ll_imageName);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

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

        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

说明:

systemMethod_PrintLog:被替换方法ll_imageName:替换方法

class_addMethod:如果发现方法已经存在,会失败返回,也可以用来做检查用,我们这里是为了避免源方法没有实现的情况;如果方法没有存在,我们则先尝试添加被替换的方法的实现

  • 1.如果返回成功: 则说明被替换方法没有存在.也就是被替换的方法没有被实现,我们需要先把这个方法实现,然后再执行我们想要的效果,用我们自定义的方法去替换被替换的方法. 这里使用到的是class_replaceMethod这个方法. class_replaceMethod本身会尝试调用class_addMethodmethod_setImplementation,所以直接调用class_replaceMethod就可以了)

  • 2.如果返回失败:则说明被替换方法已经存在.直接将两个方法的实现交换即

另外:

我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP
我们可以利用 class_replaceMethod 来修改类
我们可以利用 method_setImplementation 来直接设置某个方法的IMP
其实我们如果 研究过 AFN 代码的话,会发现, AFN 就是第二种写法.在AFURLSessionManager.m的第296行:

static inline void af_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) {
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

你可能感兴趣的:(Runtime 方法交换)