Method swizzling

iOS Method swizzling

原理
1.发生在运行时,主要用于在运行时将两个方法交换。
2.代码可以写在任何地方,但是只有在这段交换代码执行完毕之后才起作用。
3.也是面向AOP(面向切面编程)的一种实现方式。

在OC语言的runtime特性中,调用一个对象的方法就是给这个对象发送消息,是通过查找接收消息对象的方法列表,从方法列表中查找对应的SEL,这个SEL对应一个IMP(一个IMP对应多个SEL),通过这个IMP找到对应的方法调用。

在每个类中都有一个dispatch table,这个Dispatch Table本质是将类中的SEL和IMP(可以理解为函数指针)进行对应。而我们的Method Swizzling就是对这个table进行了操作,让SEL对应另一个IMP。

使用

+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class aclass = [self class];
        
        SEL originalSelector = @selector(printName);
        SEL swizzlingSelector = @selector(printSex);
        
        Method originalMethod = class_getInstanceMethod(aclass, originalSelector);
        Method swizzlingMethod = class_getInstanceMethod(aclass, swizzlingSelector);
        method_exchangeImplementations(originalMethod, swizzlingMethod);
    });
}

直接交换IMP是比较危险的。因为如果这个类中如果没有实现这个方法,class_getInstanceMethod()返回的是父类的method对象,这样交换的话,会把父类的原始实现(IMP)和这个类的Swizzle交换了,这样就会出现问题。

所以如果类中没有实现 Original selector 对应的方法,那就先添加 Method,并将其 IMP 映射为 Swizzle 的实现。然后替换 Swizzle selector 的 IMP 为 Original 的实现;否则交换二者 IMP

+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class aclass = [self class];
        
        SEL originalSelector = @selector(printName);
        SEL swizzlingSelector = @selector(printSex);
        
        Method originalMethod = class_getInstanceMethod(aclass, originalSelector);
        Method swizzlingMethod = class_getInstanceMethod(aclass, swizzlingSelector);
        
        BOOL didAddMethod = class_addMethod(aclass, originalSelector, method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
        
        if (didAddMethod) {
            class_replaceMethod(aclass, swizzlingSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }else{
            method_exchangeImplementations(originalMethod, swizzlingMethod);
        }
        
    });
}