class_addMethod

文档:

/** 
  * 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

你可能感兴趣的:(class_addMethod)