1.方法的交换使用
那么在什么情况下需要使用到这个呢,让我来举个栗子🌰,请看以下代码,如果当照片的名字是错误的话或者为空的话我们并不能及时的知道呢
UIImage *image = [UIImage imageNamed:@"123"];
首先想到的处理办法可能是,写个UIImage的扩充类别,就叫做UIImage+image吧,然后自己定义个方法进行判断,如下代码:
+(UIImage *)dm_imageNamed:(NSString *)imageName{
UIImage *image = [UIImage imageNamed:imageName];
if (image == nil) {
NSLog(@"加载image为空");
return nil;
}else{
return image;
}
}
可是每次调用上述代码,都需要导入这个头文件,会比较麻烦,这时候我们就可以用到runtime解决这个问题,我们可以将系统的imageNamed:
和我们自己定义的dm_imageNamed:
的方法进行调换,即调用系统的方法就在调用自定义的方法.需要在分类中加入这个方法:
+(void)load{
//记载这个分类的时候调用
NSLog(@"*****%s",__func__);
//获取类方法
//class_getClassMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>)
//获取对象方法
//class_getInstanceMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>)
//Class获取哪个类的方法,SEL:获取方法编号,根据SEL就能去对应的类找方法
Method imageName = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method dm_imageName = class_getClassMethod([UIImage class], @selector(dm_imageNamed:));
method_exchangeImplementations(imageName, dm_imageName);
}
这时候两个方法就进行互换了,注意,我们还需要将dm_imageNamed:的方法中改成下面这句,否则会造成循环引用.
UIImage *image = [UIImage dm_imageNamed:imageName];
这样,我们就可以尽情的调用imageNamed:
方法,实际上执行的是dm_imageNamed:
这个方法.
2.动态添加方法(懒加载模式)
新建一个Person继承于NSObject,我们在VC中执行如下代码,
Person *pp = [[Person alloc]init];
//performSelector:可以根据方法名找到对应方法
[pp performSelector:@selector(eat)];
动态添加方法,首先要实现resolveClassMethod,当调用了没有实现的方法,那么就会走这个方法,当然,方法没有的话程序会崩溃的哦,所以打断点看结果嘛.在Person.m文件写入如下代码:
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(eat)) {
//打印出的就是eat这个方法
NSLog(@"%@",NSStringFromSelector(sel));
return YES;
}
return YES;
}
接下来就说说用runtime如何动态实现,我们这里以一个可以传入参数的函数为栗子🌰
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(eat:)) {
NSLog(@"啦啦啦啦");
/*class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
参数分别对应:给哪个类添加方法; 添加方法的编号是什么;方法的实现,函数的入口,即指针;方法类型)
*/
class_addMethod(self, sel, eat, "v@:@");
return YES;
}
return YES;
}
其中方法类型这项我们可以参考官方文档
我们可以看到
V@:@
就代表一个有参无返回的函数.接下来我们需要实现这个函数,并且可以调用[pp performSelector:@selector(eat:) withObject:@1111]
的方法对结果进行分析.
void eat(id self,SEL _cmd,id param){
//其中参数(id,SEL),调用方法,系统会默认传入这个隐式参数
NSLog(@"%@,%@",param,NSStringFromSelector(_cmd));
}
3.动态方法添加属性
当我们创建一个分类,并且给分类添加属性,进行相应的调用(catagory本来就不能为原有类添加属性, 只能添加方法),我们可以通过runtime进行属性绑定,我们创建一个类别并且申明@property (nonatomic, strong) NSString *name
接下来重写set和get方法实现.
-(void)setName:(NSString *)name{
/*
参数分别对应:给某个对象添加属性;属性名,根据key去获取关联的对象;关联的值;策略(这里是传入字符串)
*/
objc_setAssociatedObject(self,@"name", name, OBJC_ASSOCIATION_RETAIN);
}
-(NSString *)name{
return objc_getAssociatedObject(self, @"name");
}
第一次写,有不足的地方欢迎指正呢.我也在学习runtime中嘞.欢迎交流哦!
以上😄