Runtime系列导读
简介
Method Swizzling,顾名思义,就是交换两个 方法的实现。简单来说,就是利用Objective-C Runtime的动态绑定特性,将一个方法的实现与另 一个方法的实现进行交换。
分析
数据结构
- objc_method
-
我们从上面的结构体中发现一个objc_method字段,它的定义如下:
image.png
-
- 我们从上面的结构体中还发现,一个方法由如下三部分组成。
- method_name:方法名。
- method_types:方法类型。
- method_imp:方法实现。
交换原理
- 使用Method Swizzling交换方法,其实就是修改了objc_method 结构体中的mthod_imp,即改变了method_name和method_imp的映射关系如下:
- 看一个例子
@implementation NSObject (Swizzler)
+ (void)swizzleInstanceMethodWithOriginalSEL:(SEL)originalSel SwizzleNewSEL:(SEL)newSel {
Method originalMethod = class_getInstanceMethod(self, originalSel);
Method newMethod = class_getInstanceMethod(self, newSel);
if (!originalMethod || !newMethod) {
return;
}
//加一层保护措施,如果添加成功,则表示该方法不存在于本类,而是存在于父类中,不能交换父类的方法,否则父类的对象调用该方法会crash;添加失败则表示本类存在该方法
BOOL addMethod = class_addMethod(self, originalSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
if (addMethod) {
//再将原有的实现替换到swizzledMethod方法上,从而实现方法的交换,并且未影响到父类方法的实现
class_replaceMethod(self, newSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, newMethod);
}
}
@end
答疑
如何hook一个对象的方法,而不影响其它对象
设置isa指向的ClassClass object_setClass(id obj, Class cls)
GXPerson* person = [GXPerson new];
[person test];
object_setClass(person, [GXPerson2 class]);
[person test];
class_replaceMethod和class_addMethod区别
- class_replaceMethod是替换某个类的方法的实现,功能上可以替代class_addMethod, 但是class_addMethod只能在SEL没有IMP指向时才可以添加成功,而class_replaceMethod不管SEL 有没有`IMP实现,都可以添加成功
class_replaceMethod和method_exchangeImplementations的不同
- 当使用class_replaceMethod替换父类的方法时,只有在子类里面调用才有效,实际上是在子类里面重写了父类的方法
- method_exchangeImplementations 是交换方法的实现,当获取的方法来自于父类时,也会造成父类调用时被触发, 交换方法的实现,由于方法的实现来自于父类,所以实际是交换的父类的方法的实现
网友评论