iOS-OC|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
【iOS-OC|iOS-Runtime之class_addMethod给类动态添加方法】实现这个方法的函数
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-OC|iOS-Runtime之class_addMethod给类动态添加方法
文章图片

注意点:
用这个方法添加的方法是无法直接调用的,必须用performSelector:调用。
因为performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译是会自动校验。

参考文章:
如何实现类方法“ + (BOOL)resolveClassMethod:(SEL) sel”
iOS Runntime 动态添加类方法并调用-class_addMethod

    推荐阅读