Runtime能做非常非常多的事情,但是不能为了使用runtime而使用,因为使用runtime会使代码的阅读性降低,使用也不方便。只能在不得已的情况下使用。
以下介绍几个用法。
导入#import <objc/message.h> 或者 #import <objc/runtime.h>
一、发消息objc_msgSend();
a、获取一个类 objc_getClass();
b、注册一个方法 sel_registerName();
c、获取一个方法 sel_getUid();
//创建一个对象
//NSObject * obj = [NSObject alloc];
//id obj = objc_msgSend([NSObject class], @selector(alloc));
//发生消息,调用alloc方法,和上面两句代码都是等价的,他的作用都是一样
id obj = objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc"));
//obj = [obj init];
//obj = objc_msgSend(obj, @selector(init));
//发生消息,调用init方法,和上面两句代码都是等价的,他的作用都是一样
obj = objc_msgSend(obj, sel_registerName("init"));/*
objc_getClass("类名") 获取一个类
sel_registerName("方法名") 注册一个方法
*/
二、方法交换
这里拿UIImage为例,添加一个UIImage的分类UIImage+jh.m
重新类方法+load
+(void)load{
//根据类获取类方法imageNamed
Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
//根据类获取类方法xg_imageNamed
Method xg_imageNamedMethod = class_getClassMethod(self, @selector(xg_imageNamed:));
//交换方法
method_exchangeImplementations(imageNamedMethod, xg_imageNamedMethod);
}
写一个交换的方法xg_imageName:
+(UIImage *)xg_imageNamed:(NSString *)name{
//这里调用xg_imageNamed: 不是调用当前这个方法,因为我们在load中已经交换了imageNamed:这个方法,所以,这里调用xg_imageNamed:就会调用imageNamed:方法。
UIImage * image = [UIImage xg_imageNamed:name];
if (image) {
NSLog(@"%@图片加载成功!",name);
}else{
NSLog(@"%@图片加载失败!",name);
}
return image;
};
所以我们在使用系统方法imageNamed:的时候就会调用xg_imageNamed:这个方法。
UIImageUIImageimageNamed:@"1.png"];
这样能够判断一个图片是否加载成功,而不需要修改系统方法,也不需要修改其他代码,方便扩展和开发。
三、动态添加属性
比如我需要给系统的NSObject类添加一个属性,那么大家都会想到使用分类添加一个属性,然后手动写setting和getting方法。
使用runtime也是一样,不过在写setting和getting方法的时候不太一样,使用runtime会方便非常多。比如加一个name的方法
添加一个NSObject+pro分类
NSObject+pro.h
#import <Foundation/Foundation.h>
@interface NSObject (pro)
/**
添加一个name属性
*/
@property NSString * name;
@end
NSObject+pro.m
#import "NSObject+pro.h"
#import <objc/message.h>
@implementation NSObject (pro)
//写setting方法
-(void)setName:(NSString *)name{
//设置一个关联 ,因为我们所有的属性其实就是一个关联
//objc_setAssociatedObject(给哪个对象添加关联, @"关联名称", 关联的值, 修饰(我这里name是一个字符串,所有使用了copy));
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
//写getting方法
-(NSString *)name{
return objc_getAssociatedObject(self, @"name");
}
@end
使用的时候就可以直接.name了
NSObject * object = [[NSObject alloc]init];
object.name = @"123";
NSLog(@"name:%@",object.name);
四、动态添加方法
创建一个Person : NSObject 类
在viewDidLoad方法执行以下代码
Person * p = [[Person alloc]init];
//这里调用eat:方法,但是在person中并没有这个方法,而在运行的使用动态创建
[p performSelector:@selector(eat:) withObject:@"apple"];
//这里调用run方法并且返回一个结果,但是在person中并没有这个方法,而在运行的使用动态创建
NSNumber * i = [p performSelector:@selector(run)];
NSLog(@"p run :%@",i);
那么来看看是怎么动态添加
person.h文件是空的,什么都没有。
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
主要看person.m文件
#import "Person.h"
#import <objc/message.h>
@implementation Person
//eat:方法的实现
void tEat(id self , SEL _cmd ,NSString * e){
NSLog(@"Person eat :%@",e);
}
//run方法的实现
NSNumber * tRun(id self , SEL _cmd){
return @100;
}
//但运行的时候,找不到某个方法就会执行
+(BOOL)resolveInstanceMethod:(SEL)sel{
//判断是不是找不到eat:方法
if (sel == NSSelectorFromString(@"eat:")) {
/*
添加方法
*/
//class :给谁(那个类)添加方法
//SEL :添加什么方法
//imp :方法的实现 -》函数 - 》 函数入口 - 》 函数名
//type :方法类型 方法类型怎么描述查看开发文档 Objective-C Runtime Programming Guide > Type Encodings.可以按住option键点击下面class_addMethod()函数,在方法说明里面用“不是黑色的字体”描述,可以直接跳转到对应说明
class_addMethod(self, sel, (IMP)tEat, "v@:@");
return YES;
}
//判断是不是找不到run 方法
if (sel == NSSelectorFromString(@"run")) {
class_addMethod(self, sel, (IMP)tRun, "@@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
五、获取成员变量和类型
添加一个分类 NSObject+runtime
NSObject+runtime.h文件
#import <Foundation/Foundation.h>
@interface NSObject (runtime)
//获取所有的成员变量
+(instancetype)getAllIvar;
@end
NSObject+runtime.m文件
#import "NSObject+runtime.h"
#import <objc/message.h>
@implementation NSObject (runtime)
+(instancetype)getAllIvar{
id obj = [[self alloc]init];
unsigned int outCount = 0;
//获取成员变量列表
Ivar * varList = class_copyIvarList(self, &outCount);
for (int i = 0; i< outCount; i++) {
Ivar ivar = varList[i];
//获取成员变量名
NSString * ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
ivarName = [ivarName substringFromIndex:1];
NSLog(@"成员变量名称:%@",ivarName);
//给属性赋值,value不可以为nil
id value;
//现在假设value 不为nil
[obj setValue:value forKey:ivarName];
//获取成员变量类型
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
//跟成员变量类型 得到对应的类
Class class = NSClassFromString(ivarType);
NSLog(@"成员变量的类型是:%@",class);
}
return obj;
}
@end