替換方法的作用一般是在原方法的基礎上加上我們想要加的功能,就是改變其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);
}