开篇
关于Runtime,在平时的开发中,我用得最多的就是方法混淆(Method Swizzling)和动态添加属性.虽然自己经常用,但是很少去深究原理性的东西.今天就重温一下费脑的runtime;
使用场景
runtime的使用场景我总结一下,大致分为如下几种,由浅入深.并且附上相关示例代码:
- 1.给分类添加属性
目前常用的两种方式,一种是显示定义定义属性关联的key,一种是直接通过方法名作为属性关于的key.
显示定义关联的key
** 直接通过方法名定义关联的key**
- 2.方法混淆(Method Swizzling)
方法混淆在实际开发中我就用到统计每个页面进入与退出的时候.其实完全把方法混洗的代码抽出来成为一个工具.如下:
这种写法稍微要严谨一点
说一说Method,SEL,IMP之间的关系,比较重要,我们直接来看,Method是runtime内部定义的方法
网上找了几张图,分析一下具体的实现过程
- 3.动态添加方法(消息转发)
一般处理的就是当类中没有定义方法,而调用了会包方法找不到的错误.可以通过消息转发搞定这个异常.具体的消息转发可以参考这里.来个图吧.
.在实际应用中一般就走到第一步就OK了.
- 4.字典转模型
字典转模型我一般比较少用,
这段代码已经用过好多次,josnModel里面已经写烂了.我也只是一名搬运工
+ (instancetype)modelWithDict:(NSDictionary *)dict
{
// 思路:遍历模型中所有属性-》使用运行时
// 0.创建对应的对象
id objc = [[self alloc] init];
// 1.利用runtime给对象中的成员属性赋值
// class_copyIvarList:获取类中的所有成员属性
// Ivar:成员属性的意思
// 第一个参数:表示获取哪个类中的成员属性
// 第二个参数:表示这个类有多少成员属性,传入一个Int变量地址,会自动给这个变量赋值
// 返回值Ivar *:指的是一个ivar数组,会把所有成员属性放在一个数组中,通过返回的数组就能全部获取到。
/* 类似下面这种写法
Ivar ivar;
Ivar ivar1;
Ivar ivar2;
// 定义一个ivar的数组a
Ivar a[] = {ivar,ivar1,ivar2};
// 用一个Ivar *指针指向数组第一个元素
Ivar *ivarList = a;
// 根据指针访问数组第一个元素
ivarList[0];
*/
unsigned int count;
// 获取类中的所有成员属性
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// 根据角标,从数组取出对应的成员属性
Ivar ivar = ivarList[i];
// 获取成员属性名
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 处理成员属性名->字典中的key
// 从第一个角标开始截取
NSString *key = [name substringFromIndex:1];
// 根据成员属性名去字典中查找对应的value
id value = dict[key];
// 二级转换:如果字典中还有字典,也需要把对应的字典转换成模型
// 判断下value是否是字典
if ([value isKindOfClass:[NSDictionary class]]) {
// 字典转模型
// 获取模型的类对象,调用modelWithDict
// 模型的类名已知,就是成员属性的类型
// 获取成员属性类型
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// 生成的是这种@"@\"User\"" 类型 -》 @"User" 在OC字符串中 \" -> ",\是转义的意思,不占用字符
// 裁剪类型字符串
NSRange range = [type rangeOfString:@"\""];
type = [type substringFromIndex:range.location + range.length];
range = [type rangeOfString:@"\""];
// 裁剪到哪个角标,不包括当前角标
type = [type substringToIndex:range.location];
// 根据字符串类名生成类对象
Class modelClass = NSClassFromString(type);
if (modelClass) { // 有对应的模型才需要转
// 把字典转模型
value = [modelClass modelWithDict:value];
}
}
// 三级转换:NSArray中也是字典,把数组中的字典转换成模型.
// 判断值是否是数组
if ([value isKindOfClass:[NSArray class]]) {
// 判断对应类有没有实现字典数组转模型数组的协议
if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
// 转换成id类型,就能调用任何对象的方法
id idSelf = self;
// 获取数组中字典对应的模型
NSString *type = [idSelf arrayContainModelClass][key];
// 生成模型
Class classModel = NSClassFromString(type);
NSMutableArray *arrM = [NSMutableArray array];
// 遍历字典数组,生成模型数组
for (NSDictionary *dict in value) {
// 字典转模型
id model = [classModel modelWithDict:dict];
[arrM addObject:model];
}
// 把模型数组赋值给value
value = arrM;
}
}
if (value) { // 有值,才需要给模型的属性赋值
// 利用KVC给模型中的属性赋值
[objc setValue:value forKey:key];
}
}
return objc;
}
### 总结
这篇简单讲了一下,runtime在开发中用到的场景.属于应用层面,下篇开始讲原理
参考链接
[快速上手Runtime](http://www.jianshu.com/p/e071206103a4)