有这样一个需求:
类Target,声明两个类Target的实例targetA,targetB;
当实例targetA调用方法oriMethod时,调用oriMethod;
当实例targetB调用方法oriMethod时,则实际调用的是hook之后的方法targetMethod
Target *targetA = [[Target alloc] init];
Target *targetB = [[Target alloc] init];
[targetA oriMethod]; //输出:原方法oriMethod
[targetB oriMethod]; //输出:新方法targetMethod
当面对上述需求时,Objective-C的动态性优势就显示出来啦;
具体实现原理类同KVO底层原理,给targetB类添加观察者,在观察方法中动态的生成一个中间类,然后在中间类中将方法oriMethod和targetMethod进行methodSwizzing;
具体实现步骤:
创建一个NSObject的Category,(NSObject+Swizzing.h),在类中添加两个方法
/// 添加观察方法
/// @param observer 目标类
/// @param oriMethod 原始方法
/// @param targetMethod 目标替换方法
- (void)ltAddObserver:(NSObject*)observer forOriMethod:(NSString*)oriMethod byNewMethod:(NSString*)targetMethod;
/// 移除观察方法
/// @param observer 目标类
/// @param oriMethod 原始方法
- (void)ltRemoveObserver:(NSObject*)observer forOriMethod:(NSString*)oriMethod;
判断原始方法(oriMethod)是否存在,如果不存在,那么直接返回,没必要进行后续操作
- (BOOL)judgeMethodIsExist:(NSString*)method{
Class curClass =object_getClass(self);
SEL oriSel =NSSelectorFromString(method);
Method oriMethod =class_getInstanceMethod(curClass, oriSel);
if(!oriMethod) {
return false;
}
return true;
}
动态生成中间类
- (Class)dynamicCreateClass{
NSString*oriClassName =NSStringFromClass([selfclass]);
NSString*newClassName = [NSString stringWithFormat:@"%@%@",methodPrefix, oriClassName];
Class newClass =NSClassFromString(newClassName);
//申请类
newClass =objc_allocateClassPair([self class], newClassName.UTF8String,0);
//注册
objc_registerClassPair(newClass);
//添加class方法(class用来修改isa)
SEL classSel =@selector(class);
Method classMethod =class_getInstanceMethod([self class], classSel);
const char *classType =method_getTypeEncoding(classMethod);
class_addMethod(newClass, classSel, (IMP)lt_class, classType);
//添加dealloc方法
SEL deallocSel =NSSelectorFromString(@"dealloc");
Method deallocMethod =class_getInstanceMethod([self class], deallocSel);
const char*deallocType =method_getTypeEncoding(deallocMethod);
class_addMethod(newClass, deallocSel, (IMP)lt_dealloc, deallocType);
//添加方法
return newClass;
}
lt_class方法实现,用来将iSA返回父类
Class lt_class(id self,SEL _cmd){
return class_getSuperclass(object_getClass(self));
}
lt_dealloc方法,对象释放会自动调用
void lt_dealloc(id self,SEL _cmd){
Class superClass = [self class];
object_setClass(self, superClass);
}
交换的核心方法
+ (BOOL)lt_hookOriInstanceMethod:(Class)cls oriSel:(SEL)oriSel newInstanceMethod:(SEL)newSel{
Method oriMethod =class_getInstanceMethod(cls, oriSel);
Method newMethod =class_getInstanceMethod(cls, newSel);
//如果不存在原始方法,那么添加一个空的方法,什么也不做,防止死循环
if(!oriMethod) {
class_addMethod(cls,
oriSel,method_getImplementation(newMethod),method_getTypeEncoding(newMethod));
method_setImplementation(newMethod,imp_implementationWithBlock(^(id self,SEL _cmd){}));
}
//交换方法
//步骤:
//1、先添加方法,如果添加不成功,那么说明原始类中存在方法,直接交换即可
//2、如果添加成功,说明原始类中不存在方法,那么直接replace
BOOL addMethod =class_addMethod(cls, oriSel,method_getImplementation(newMethod),method_getTypeEncoding(newMethod));
if(!addMethod) {
method_exchangeImplementations(oriMethod, newMethod);
}else{
class_replaceMethod(cls, newSel,method_getImplementation(oriMethod),method_getTypeEncoding(oriMethod));
}
return true;
}
以上就是动态生成类以及类中添加方法的核心代码;
需要注意的是:
在ltAddObserver方法中,中间类创建出来后,一定要将修改iSA指向,否则调不到;
在ltRemoveObserver方法中,主要实现在移除观察之后,将iSA指针指回去;
还里利用associatedObject将被观察类和方法添加到全局表中(存储的是model信息)
具体代码实现 喜欢记得点星星哦