runtime, 直译就是运行时机制
定义
runtime是OC底层的一套C语言API(引入<objc/runtime.h>或<objc/message.h>)
利用runtime能实现OC不容易实现的功能
1.动态交换两个方法的实现(特别是交换系统自带的方法)
2.动态添加对象的成员变量和成员方法
3.获得某个类的所有成员方法、所有成员变量
如何应用runtime
1.将某些OC代码转为运行时代码,探究底层。比如block的实现原理
2.拦截系统自带的方法调用,替换成想要实现的方法,比如拦截imageNamed:(主题替换的实现),viewDidLoad、alloc等
3.实现分类也可以增加属性;
4.实现NSCoding的自动归档解档;
5.实现了字典和模型的自动转换。
下面稍微举例说明下
一、拦截系统自带的方法调用,替换成想要实现的方法
获得某个类的类方法
Method class_getClassMethod(Class cls,Sel name)
获得某个类的实例方法
Method clss_getInstanceMethod(Class cls,sel name)
交换两个方法的实现
void method_exchangeImplementations(Method m1,Method m2)
步骤:
1、为UIImage建一个分类(UIImage+Category)
2、在分类中实现一个自定义方法,方法中写要在系统方法中加入的语句,比如版本判断
+ (UIImage *)xh_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
// 如果系统版本是7.0以上,使用另外一套文件名结尾是‘_os7’的扁平化图片
name = [name stringByAppendingString:@"_os7"];
}
return [UIImage xh_imageNamed:name];
}
// 获取两个类的类方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
// 开始交换方法实现
method_exchangeImplementations(m1, m2);
// 交换后,先打印学习,再打印跑!
[Person run];
[Person study];
会发现方法已被调换
二、在分类中设置属性,给任何一个对象设置属性
众所周知,分类是无法设置属性的,如果在分类的声明中写@property只能为其生成get和set方法的声明,但无法生成成员变量,就是虽然点语法能调用出来,但程序执行后会crash。这时就可以用runtime实现。
步骤:
1、创建一个分类,比如给任何一个对象都添加一个name属性,就是NSObject添加分类(NSObject+Category)
2、现在.h中@property声明出get和set方法,方便点语法调用
@property(nonatomic,copy)NSString *name;
3、在.m中重写set和get方法,内部利用runtime给属性赋值和取值
char nameKey;
- (void)setName:(NSString *)name {
// 将某个值跟某个对象关联起来,将某个值存储到某个对象中
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
三、获得一个类的所有成员变量
Ivar *class_copyIvarList(Class cls,unsigned int *outCount)
//获得成员变量的名字
const char *ivar_getName(Ivar v)
//获得成员变量的类型
const char *ivar_getTypeEndcoding(Ivar v)
案例1:获取Person类中所有成员变量的名字和类型
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([Person class], &outCount);
// 遍历所有成员变量
for (int i = 0; i < outCount; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"成员变量名:%s 成员变量类型:%s",name,type);
}
// 注意释放内存!
free(ivars);
依据上面的原理我们就可以给NSObject做一个分类,利用runtime 获取所有属性来重写归档解档方法,让我们不需要每次都写这么一长串代码,只要实现一小段代码就可以让一个对象具有归解档的能力。
还可以利用runtime将字典装模型