runtime之动态添加方法

如果一个类接收到一个没有实现的实例方法,就会进到下面这个方法里+(BOOL)resolveInstanceMethod:(SEL)sel,同理类方法会进到这个+(BOOL)resolveClassMethod:(SEL)sel

1. 验证

首先创建一个Student类然后调用一个没实现的learn方法:

    Student *s = [[Student alloc] init];
    [s performSelector:@selector(learn)];

然后在Student.m中拦截一下,打印方法名:

// 如果该类接收到一个没有实现的实例方法,就会进到这个方法里
+(BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"%@",NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}

打印

runtime_动态添加方法[2398:288368] learn

2. 动态添加方法

接下来,拦截到以后动态添加一个方法,用runtime的class_addMethod方法

// 动态添加方法
    /*
     1、cls 给哪个类添加方法
     2、SEL 方法编号
     3、IMP 函数指针
     4、返回值类型
     */
    class_addMethod(<#Class  _Nullable __unsafe_unretained cls#>, <#SEL  _Nonnull name#>, <#IMP  _Nonnull imp#>, <#const char * _Nullable types#>)

前边三个参数都好理解,关于第四个参数,苹果的文档里有介绍,command+shift+0打开文档,搜索class_addMethod,下面是示例代码和截图

class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:");
image.png

然后在resolveInstanceMethod动态添加一个方法,在用speak方法接收处理:

+(BOOL)resolveInstanceMethod:(SEL)sel {
    
    NSLog(@"%@",NSStringFromSelector(sel));
    // 动态添加方法
    class_addMethod(self, sel, (IMP)speak, "v@:");
    
    return [super resolveInstanceMethod:sel];
}

void speak() {
    NSLog(@"speak");
}

打印

runtime_动态添加方法[2430:292288] learn
runtime_动态添加方法[2430:292288] speak

动态创建方法成功

3. 传参数

OC的方法调用,会默认传递两个隐式参数给IMP(方法实现),文档里有示例代码,截图


image.png

首先调用时传个参数:

    Student *s = [[Student alloc] init];
    [s performSelector:@selector(learn) withObject:@"English"];

也可以换成发送消息(这里不懂可以看我之前的关于消息转发的),这里的参数和我们接收时的参数是一一对应的,便于理解

    Student *s = [[Student alloc] init];
    objc_msgSend(s, sel_registerName("learn"), @"English");

这里class_addMethod函数的第四个参数需要改成"v@:@",接收的speak函数需要把隐式参数都加上,才可以收到我们传的参数:

// 如果该类接收到一个没有实现的实例方法,就会进到这个方法里
+(BOOL)resolveInstanceMethod:(SEL)sel {
    
    NSLog(@"%@",NSStringFromSelector(sel));
    // 动态添加方法
    class_addMethod(self, sel, (IMP)speak, "v@:@");
    
    return [super resolveInstanceMethod:sel];
}

/**
 OC的方法调用,会默认传递两个隐式参数给IMP(方法实现)
 objc_msgSend(self,_cmd)
 id self  方法的调用者
 SEL _cmd 方法编号
 */
void speak(id class, SEL sel, NSString *objc) {
    NSLog(@"%@-%@-%@",NSStringFromClass([class class]),NSStringFromSelector(sel),objc);
}

打印

runtime_动态添加方法[2458:295930] learn
runtime_动态添加方法[2458:295930] Student-learn-English

对照一下发消息和接收函数的参数:

// 发消息
objc_msgSend(s, sel_registerName("learn"), @"English");
// 接收函数
void speak(id class, SEL sel, NSString *objc)

完!!!

你可能感兴趣的:(runtime之动态添加方法)