一, runtime的动态添加方法功能
1, 关于runtime动态添加方法,我们以一个经典的面试题展开对其的研究.
-
经典面试题 : 有没有使用过performSelector方法, 什么时候调用该方法?
- 答 : 肯定是要回答使用过,不然就没有然后了........其实面试官主要是想问问你有没有动态添加过方法,下面我们就简单的学习runtime动态添加方法.
-
2, 首先我们需要清除一个问题即: 为什么要动态添加方法,它的作用是什么,以及调用时刻是什么时候?
答 : 在OC中,所有的控件(textFiled或者button等控件), 数组, 数据等都是以懒加载的形式加载的.真正使用的时候才会加载,或者添加方法.动态添加的方法的作用就是去处理未实现的实例方法或者是类方法.它的调用时刻: 只要我们调用了一个不存在的方法时,它就会动态添加方法.每个类都一个处理为实现方法的方法.
二, 步骤
1, 新建一个学生类(WGStudent)在该类中只声明一个实例方法,不去实现,看看外界调用该方法时,会造成什么样的后果.
下面这个方法是错误的演示
在 WGStudent.h文件中
#import <Foundation/Foundation.h>
@interface WGStudent : NSObject
// 如果该方法只有声明没有实现,那么该方法一定不会放在方法列表中,外界一定不能直接调用到方法
- (void)study;
@end
在ViewController.h文件中
#import "ViewController.h"
#import "WGStudent.h"
#import <objc/message.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WGStudent *student = [[WGStudent alloc] init];
objc_msgSend(student, @selector(study));
}
@end
- 打印结果
// 根本找不到这个方法
reason: '-[WGStudent study]: unrecognized selector sent to instance 0x7f94bb46e070'
- 2, 这时候就需要使用到runtime动态添加方法了.在动态添加方法之前我们还需要判断当前的方法有没有实现,如果没有实现才需要动态添加方法.所以, 我们就需要了解下面两个方法:
// 动态去判断下eat方法有没有实现,如果没有实现,动态添加.
// 作用:处理未实现的对象方法
// 调用时刻:只要调用了一个不存在的对象方法就会调用
// sel:就是未实现方法编号
// 判断对象方法有没有实现
+(BOOL)resolveInstanceMethod:(SEL)sel
// 判断类方法有没有实现
+ (BOOL)resolveClassMethod:(SEL)sel
- 3, 进入苹果系统内部查看如何动态添加方法(这是官方文档中的方法)
// dynamicMethodIMP方法
// 动态添加这个dynamicMethodIMP方法
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
// 苹果内部的动态添加方法
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
- 了解几个与动态添加方法有关的几个参数
// 参数解释:
// Class;给哪个类添加方法
// SEL:添加方法
// IMP:方法实现,函数名
// types:方法类型(不要去死记,官方文档中有)
class_addMethod(__unsafe_unretained Class cls, SEL name, IMP imp, const char *types)
- 注意 : 动态添加方法的第三个参数是一个函数名,所以需要定义一个C语言函数,注意括号中的两个隐式参数,不能少,一下是给"学生类"动态添加方法,步骤都是按照苹果官方文档来创建的.
- 2, 给学生动态添加一个学习英语的方法
在WGStudent.m文件中
// 模仿官方文档来动态添加方法
#import "WGStudent.h"
#import <objc/message.h>
@implementation WGStudent
void studyEngilsh(id self, SEL _cmd) {
NSLog(@"动态添加了一个学习英语的方法");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == NSSelectorFromString(@"studyEngilsh")) {
// 注意:这里需要强转成IMP类型
class_addMethod(self, sel, (IMP)studyEngilsh, "v@:");
return YES;
}
// 先恢复, 不然会覆盖系统的方法
return [super resolveInstanceMethod:sel];
}
@end
在ViewController.m文件中
#import "ViewController.h"
#import "WGStudent.h"
#import <objc/message.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WGStudent *student = [[WGStudent alloc] init];
[student performSelector:@selector(studyEngilsh)];
}
@end
- 打印结果
// 说明动态添加方法成功
2016-03-08 20:07:23.160 sasass[1280:35172] 动态添加了一个学习英语的方法
-
注意点:
- 1, type(方法类型)用到时直接去官方文档中有详细的介绍
- 2, C函数中两个隐式参数的意思 :
- 1, self:方法调用者
- _cmd:当前调用方法编号
- 方法的隐式参数即: 没有暴露出来参数.