首先创建一个初始化工程,直接对 ViewController 进行实战即可,在 ViewController 中加入一个 eat 方法,如下
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
}
- (void)eat {
NSLog(@"original eat");
}
然后写一个 NSObject 的 Category 负责进行方法交换,将原对象的 isa 指针指向该对象类的子类(LDSubclass),并在子类中重写 eat 方法
@implementation NSObject (Hook)
+ (void)hookWithInstance:(id)instance method:(SEL)selector {
Method originMethod = class_getInstanceMethod([instance class], selector);
if (!originMethod) {
// exception ..
}
Class newClass = [LDSubclass class];
// 修改 isa 指针的指向
object_setClass(instance, newClass);
}
@end
子类的代码很简单,就是重写 eat 方法,如果有需要,可以调用原方法的实现
@implementation LDSubclass
- (void)eat {
NSLog(@"newSubClass eat");
struct objc_super superClazz = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self))
};
// 调用原方法
void (*objc_msgSendSuperCasted)(void *, SEL) = (void *)objc_msgSendSuper;
objc_msgSendSuperCasted(&superClazz, _cmd);
}
@end
最后在 ViewControlller 中进行测试即可,此时的 ViewController 代码如下
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
ViewController * vc = [[ViewController alloc] init];
[vc eat];
NSLog(@"-----------");
ViewController * hookedInstance= [[ViewController alloc] init];
[ViewController hookWithInstance:hookedInstance method:@selector(eat)];
[hookedInstance eat];
}
- (void)eat {
NSLog(@"original eat");
}
@end
来看看打印的结果,第一个没有 Hook 的实例,正常执行;第二个Hook 后的实例,先执行重写的方法,后执行原方法。