iOS-Runtime之class_addMethod给类动态添加方法

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 _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

描述就是:给一个类添加一个新的方法和该方法的具体实现

BOOL: 返回值,YES:方法添加成功 ;NO:方法添加失败

参数:

1、Class cls

cls :要添加新方法的那个类;

传的类型 [类名  class]

实例方法,传入CLass;类方法,传入MetaClss;可以这样理解,OC里的Class里的加号方法,相当于该类的MetalClas的实例方法,类调用类方法,和对象调用实例方法,其实底层实现都是一样的。类也是对象。

2、SEL name

name :要添加的方法名;

添加的方法在本类里面叫的名字,但是方法的格式一定要和你需要添加的方法的格式一样,比如有无参数

传的类型   @selector(方法名)

3、IMP imp

实现这个方法的函数
imp:指向实现方法的指针   就是要添加的方法的实现部分

IMP就是Implementation的缩写,它是指向一个方法实现的指针,每一个方法都有一个对应的IMP。这里需要的是IMP,所以不能直接写方法,需要用到一个方法:class_getMethodImplementation

SEL就是一个函数的声明方法,而IMP就是这个方法的实现,也就是一个函数的指针

传的类型  

(1)C语言写法:(IMP)方法名  ,举例如下:

不带参数:

//C语言函数
void startEngine(id self, SEL _cmd) {
    NSLog(@"my car starts the engine");
}

这是一个 C 语言的函数,它至少包含了 self 和 _cmd 两个参数(self 代表着函数本身,而 _cmd 则是一个 SEL 数据体,包含了具体的方法地址)。如果要在这个方法中新增参数如下

带参数:

//C语言函数
void startEngine(id self, SEL _cmd, NSString *brand) {
    NSLog(@"my %@ car starts the engine", brand);
}

@implementation Car (myCar)

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(drive)) {
        class_addMethod([self class], sel, (IMP)startEngine, "v@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end

只要在那两个必须的参数之后添加所需要的参数和类型就可以了,返回值同理,只要把方法名之前的 void 修改成我们想要的返回类型即可
(2)OC的写法:class_getMethodImplementation(self,@selector(方法名:)){ };如下:

OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

这个方法也是runtime的方法,就是获得对应的方法的指针,也就是IMP。

OC写法举例如下:

@implementation Car (myCar)

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(drive)) {
        class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(startEngine:)), "s@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

- (void)startEngine:(NSString *)brand {
    NSLog(@"my %@ car starts the engine", brand);
}

@end

4、const char *types
要添加的方法的返回值和参数   叫 type encodings ;这里可以参考苹果官方文档Type Encodings

"v@:@":v:是添加方法无返回值     @表示是id(也就是要添加的类) :表示添加的方法类型   @表示:参数类型

比如:”v@:”意思就是这已是一个void类型的方法,没有参数传入。
再比如 “i@:”就是说这是一个int类型的方法,没有参数传入。
再再比如”i@:@”就是说这是一个int类型的方法,有一个参数传入。

const char *types含义表如下:

iOS-Runtime之class_addMethod给类动态添加方法_第1张图片

注意点:

用这个方法添加的方法是无法直接调用的,必须用performSelector:调用。

因为performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译是会自动校验。

参考文章:

如何实现类方法“ + (BOOL)resolveClassMethod:(SEL) sel”

iOS Runntime 动态添加类方法并调用-class_addMethod

你可能感兴趣的