文档:
/**
* Adds a new method to a class with a given name and implementation.
*
* @param cls The class to which to add a method.
* @param name A selector that specifies the name of the method being added.
* @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
* @param types An array of characters that describe the types of the arguments to the method.
*
* @return YES if the method was added successfully, otherwise NO
* (for example, the class already contains a method implementation with that name).
*
* @note class_addMethod will add an override of a superclass's implementation,
* but will not replace an existing implementation in this class.
* To change an existing implementation, use method_setImplementation.
*/
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
这个方法的作用是,给类添加一个新的方法和该方法的具体实现
三个参数:
Class cls
cls 参数表示需要添加新方法的类。
SEL name
name 参数表示 selector 的方法名称,可以根据喜好自己进行命名。
IMP imp
imp 即 implementation ,表示由编译器生成的、指向实现方法的指针。也就是说,这个指针指向的方法就是我们要添加的方法。
const char *types
最后一个参数 *types 表示我们要添加的方法的返回值和参数。
objc_method中的method_types指的是方法的返回值和参数。以返回值开始,依次把参数拼在后面,比如“我@”,即为返回值为INT类型,参数为一个对象,参照对照表。
其次,我们需要知道在什么情况下需要调用 class_addMethod 这个方法。当项目中,需要继承某一个类(subclass),但是父类中并没有提供我需要的调用方法,而我又不清楚父类中某些方法的具体实现;或者,我需要为这个类写一个分类(category),在这个分类中,我可能需要替换/新增某个方法(注意:不推荐在分类中重写方法,而且也无法通过 super 来获取所谓父类的方法)。大致在这两种情况下,我们可以通过 class_addMethod 来实现我们想要的效果。
好了,说了这么多那么到底应该如何调用呢?如果不清楚使用方法,那么看说明书就是最好的方法。在 Apple 提供的文档中就有详细的使用方法(Objective-C Runtime Programming Guide - Dynamic Method Resolution)
这个机制中所涉及的方法主要有两个:
+ (BOOL)resolveInstanceMethod:(SEL)sel
+ (BOOL)resolveClassMethod:(SEL)sel
两个方法的唯一区别在于需要添加的是静态方法还是实例方法。
void b(id self, SEL _cmd) {//这是一个用来补偿的方法
NSLog(@"b");
}
一个 C 语言的函数,它至少包含了 self 和 _cmd 两个参数(self 代表着函数本身,而 _cmd 则是一个 SEL 数据体,包含了具体的方法地址)
重写方法:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(xxx)) {
class_addMethod([self class],sel, (IMP)b, "v@:");
}
return [super resolveInstanceMethod:sel];
}
具体的调用
[self performSelector:@selector(xxx)];
运行结果:
下面是带参数版本:
只要在那两个必须的参数之后添加所需要的参数和类型就可以了,返回值同样道理,只要把方法名之前的 void 修改成我们想要的返回类型就可以,这里我们不需要返回值。
void b(id self, SEL _cmd, NSString *brand) {
NSLog(@"b%@",brand);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(xxx)) {
class_addMethod([self class],sel, (IMP)b, "v@:");
}
return [super resolveInstanceMethod:sel];
}
调用:
[self performSelector:@selector(xxx) withObject:@"我是参数"];
结果:
OC函数版本
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(xxx)) {
//其中 class_getMethodImplementation 意思就是获取 SEL 的具体实现的指针。
class_addMethod([self class], sel, class_getMethodImplementation([self class], @selector(replaceSel)), "@v@:");
// class_addMethod([self class], sel, class_getMethodImplementation([self class], @selector(replaceSel2:)), "");
}
return [super resolveInstanceMethod:sel];
}
- (void)replaceSel{
NSLog(@"成功了");
}
- (void)replaceSel2:(NSString *)name{
NSLog(@"成功了%@",name);
}
带参数版本
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(xxx)) {
class_addMethod([self class], sel, class_getMethodImplementation([self class], @selector(replaceSel3:)), "");
}
return [super resolveInstanceMethod:sel];
}
- (NSString *)replaceSel3:(NSString *)name{
NSLog(@"成功了%@",name);
return [NSString stringWithFormat:@"成功了%@",name];
}
调用
NSString * myResault = [self performSelector:@selector(xxx) withObject:@"我是参数"];
NSLog(@"xxx%@xxx",myResault);
但是缺点是最多只可以传递两个参数,穿多个参数可以百度一NSInvocation
https://www.jianshu.com/p/177e44a411db