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);
}
});
}