Objective-C 中的 Hook 又被稱作 Method Swizzling饺律。
較好的開源項目 RSSwizzle 和 jrswizzle
項目中常用的方法交換寫法如下:
版本1
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class aClass = [self class];
SEL originalSelector = @selector(method_original:);
SEL swizzledSelector = @selector(method_swizzle:);
//??下面的originalMethod和swizzledMethod類似于值傳遞
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(aClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
版本2
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class aClass = [self class];
SEL originalSelector = @selector(method_original:);
SEL swizzledSelector = @selector(method_swizzle:);
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
常見的以上兩種版本声滥,版本2是版本1的縮略版妓蛮,正常情況下不會有問題,但是下面這種情況會有問題殿衰。
復(fù)現(xiàn)條件:
1. B繼承自A
2. B中沒有method_original
方法的實現(xiàn)朱庆,A有method_original
方法的實現(xiàn)
3. 在B類中進(jìn)行method_original
和method_swizzle
交換
結(jié)果:
A在調(diào)用method_original
時,實際上會調(diào)用B的method_swizzle
闷祥,此時會出現(xiàn)方法找不到的錯誤([A method_swizzle]
)
以上有個很重要的知識點:
如果在子類B中 class_addMethod
添加成功娱颊,那么originalMethod
和originalMethod2
就不想等了
//子類B的load方法
+ (void)load
{
//可以理解成值的傳遞,指向的父類的方法
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
//子類B中添加成功
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//originalMethod2指向子類新添加的方法,originalMethod還是指向父類的方法
Method originalMethod2 = class_getInstanceMethod(aClass, originalSelector);
}