替换方法的作用一般是在原方法的基础上加上我们想要加的功能,就是改变其IMP,所以我们调用的时候还是调用原方法,不需要去修改
一. 在分类中重写之前的方法(即覆盖了原方法)
在分类中重写之前的方法:
People.h
@interface People : NSObject
- (void)say ;
@end
People +Swizzling.m
#import "People +Swizzling.h"
@implementation People (Swizzling)
- (void)say {
NSLog(@"People +Swizzling.h --- say");
}
@end
但是这样做的缺点有2点:
1.在分类中重写方法会覆盖掉原方法的功能(即调用不了原方法),
2,如果多个分类都重写了一个方法,运行时机制无法确定会一直调用哪个方法;
二. 使用运行时的交换方法(method swizzling)
写一个不同名的方法, 使用运行时将该方法与原方法交换, (子类的方法列表并不包含父类中的方法)
- 分2种情况:
- 原方法为父类方法,子类没有
- 方法和替换方法都实现于子类
Method m1 = class_getInstanceMethod(self, @selector(eat));
Method m2 = class_getInstanceMethod(self, @selector(son_eat));
BOOL add = class_addMethod(self, @selector(eat), method_getImplementation(m2), method_getTypeEncoding(m2));
1.第一种情况:子类没有实现替换方法时,需要检测(将原方法名SEL和替换方法的实现IMP,加入方法到本类中), 如果能加入,这时原方法的方法实现指针指向了替换方法, 再将替换方法SEL的IMP指向原方法的IMP就完成了方法交换;
if (add) {
class_replaceMethod(self, @selector(son_eat), method_getImplementation(m1), method_getTypeEncoding(m1));
}
2.第二种情况:俩个方法都在子类中已经实现,就直接交换方法即可
else {
method_exchangeImplementations(m1, m2);
}
- (void)eat {
NSLog(@"%s", __FUNCTION__);
}
- (void)son_eat {
[self son_eat];
NSLog(@"%s", __FUNCTION__);
}
确保该代码只会调用一次,避免多次调用替换混乱
一般是在+ (void)load
方法中
三.C指针
- (方法替换的本质:将原方法的方法实现指向一个新的方法,新方法里面需要调用原方法的实现,即新方法是私有的;)
void (*gOrigDrawRect)(id, SEL, NSRect);
+ (void)load
{
Method origMethod = class_getInstanceMethod(self, @selector(drawRect:));
gOrigDrawRect = (void *)method_getImplementation(origMethod);
if(!class_addMethod(self, @selector(drawRect:), (IMP)OverrideDrawRect, method_getTypeEncoding(origMethod)))
method_setImplementation(origMethod, (IMP)OverrideDrawRect);
}
static void OverrideDrawRect(NSView *self, SEL _cmd, NSRect r)
{
gOrigDrawRect(self, _cmd, r);
[[NSColor blueColor] set];
NSRectFill(r);
}
参考
- Method Replacement for Fun and Profit