YYModel 源码解读(二)之NSObject+YYModel.h (2)
_YYModelMeta 这个内部的类主要是对这个类的描述。包含了和此类转换相关的数据。
/// A class info in object model.
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper;
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas;
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount;
/// Model class type.
YYEncodingNSType _nsType; BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
@end
YYClassInfo *_classInfo; ------------- 抽象的类信息
对这个映射的关系 ,可能大家不太了解,下边通过一个例子可以非常清晰的解释这一过程
自定义的映射关系有一下几种:
1. 一个属性名对应一个json key
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"userID" : @"id",
@"idString" : @"idstr",
}
2. 多个属性映射同一个json key
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"userID" : @"id",
@"idString" : @"id",
@"uid" : @"id",
}
3. 一个属性映射 一个 json keyPath
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"userID" : @"user.id",
}
4. 一个属性 映射 多个json key 或者 keyPath
+ (NSDictionary *)modelCustomPropertyMapper {
// 映射的数组送 包含key 也包含keyPath
return @{@"userID" : @[@"ID",@"id",@"user.id"],
}
上边的第二个情况
@{@"userID" : @"id",
@"idString" : @"id",
@"uid" : @"id",
}
中为了解决多个属性映射同一 key 的 问题, 引入了链表的 操作
-> next 指针指向下一个 属性
至于 链表的知识 请查资料了解
NSDictionary *_mapper ---------- > 来源于原始json 的 映射关系
"attitudes_count" = "<_YYModelPropertyMeta: 0x7f8f89efb7d0>";
"thumbnail" : {
"cut_type" : ,
"type" : "WEBP",
"url" : "http://ww2.sinaimg.cn/or180/eb8fce65jw1ew468zkxcgj237k1t01l0.jpg",
"width" : ,
"height" :
},
上边的代码是一个json 我们在代码中是这样写的
@property (nonatomic, strong) YYWeiboPictureMetadata *thumbnail;
那么 映射完成后的结果是
"cut_type" = "<_YYModelPropertyMeta: 0x7f8f8eb14c90>";
height = "<_YYModelPropertyMeta: 0x7f8f8eb14fa0>";
type = "<_YYModelPropertyMeta: 0x7f8f8eb14b40>";
url = "<_YYModelPropertyMeta: 0x7f8f8eb14d10>";
width = "<_YYModelPropertyMeta: 0x7f8f8eb14d90>";
key 依然是原有的json 的key 但是 value 是 在这个映射类中 包含已经映射成功后的对这个key的YYModelPropertyMeta封装
NSArray *_allPropertyMetas --------- > 包含该类和该类的所有父类 直到 NSObject/NSProxy为止的所有属性抽象类NSArray<_YYModelPropertyMeta>
NSArray *_keyPathPropertyMetas --------- > 包含 映射为keypath 的 NSArray<_YYModelPropertyMeta>
NSArray *_multiKeysPropertyMetas ----------> @{@"userID" : @[@"ID",@"id",@"user.id"] 类似这样映射,包含有数组映射的NSArray<_YYModelPropertyMeta>
在下边的这个实现方法中不但给上面介绍的 属性赋值外,还给_YYModelPropertyMeta 内的部分属性进行了赋值,代码中都有相信的说明
- (instancetype)initWithClass:(Class)cls {
// 根据类 生成 抽象的ClassInfo 类
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
// Get black list
// 黑名单,在转换过程中会忽略数组中属性
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// Get white list
// 白名单,转换过程 中处理 数组内的属性,不处理数组外的数据
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// Get container property's generic class
// 获取 容器内部制定的类型字典
/**
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"shadows" : [Shadow class],
@"borders" : Border.class,
@"attachments" : @"Attachment" };
}
经过下边转换后得到:
@{
@"shadows" : Shadow,
@"borders" : Border,
@"attachments" : Attachment
};
*/
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
if (genericMapper) {
NSMutableDictionary *tmp = [NSMutableDictionary new];
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (![key isKindOfClass:[NSString class]]) return;
Class meta = object_getClass(obj);
if (!meta) return;
if (class_isMetaClass(meta)) {
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) {
Class cls = NSClassFromString(obj);
if (cls) {
tmp[key] = cls;
}
}
}];
genericMapper = tmp;
}
}
// Create all property metas.
// 获取 所有的属性
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
/**
* 向上层便利类,知道父类为空位置,目的是获取所有的属性
*/
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
//属性名称为空 忽略
if (!propertyInfo.name) continue;
//在黑名单中 忽略
if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
// 不在白名单中忽略
if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
/**
* 创建对该条属性的抽象类
* classInfo
* propertyInfo
* genericMapper[propertyInfo.name] 容器内指定的类
*/
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
// 判断
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue;
// 如果字典中存在,忽略
if (allPropertyMetas[meta->_name]) continue;
// 给字典复制
allPropertyMetas[meta->_name] = meta;
}
// 当前的类 指向上一个类的父类
curClassInfo = curClassInfo.superClassInfo;
}
// 给本类的属性_allPropertyMetas 赋值
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
// create mapper
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
/**
* 如果实现了 modelCustomPropertyMapper 方法
*
* @param modelCustomPropertyMapper
*
*/
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
// 获取自定义的字典
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
// 遍历字典
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
// 根据名字 在 全部属性字典中取出与之相对应的属性抽象类
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
// 已经找到了结果,可以删除掉,这样在下次查找的时候,就不用做多余的遍历了 ,能够节省时间
[allPropertyMetas removeObjectForKey:propertyName];
if ([mappedToKey isKindOfClass:[NSString class]]) {
if (mappedToKey.length == ) return;
// 给抽象类的_mappedToKey 赋值 标示要被映射的名称 下边的指的就是@"n",@"p"...
/*
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"name" : @"n",
@"page" : @"p",
@"desc" : @"ext.desc",
@"bookID" : @[@"id",@"ID",@"book_id"]};
}
*/
propertyMeta->_mappedToKey = mappedToKey;
// 映射对象 如果是keypath ,@"user.id"
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
// 遍历数组 ,删除空字符串
for (NSString *onePath in keyPath) {
// 如果存在空字符 则在原数组中删除
if (onePath.length == ) {
NSMutableArray *tmp = keyPath.mutableCopy;
[tmp removeObject:@""];
keyPath = tmp;
break;
}
}
// keypath 的个数大于1 说明为 有效路径
if (keyPath.count > ) {
// 赋值
propertyMeta->_mappedToKeyPath = keyPath;
[keyPathPropertyMetas addObject:propertyMeta];
}
// 控制 propertyMeta 的 next 指针 指向下一个 映射
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
NSMutableArray *mappedToKeyArray = [NSMutableArray new];
for (NSString *oneKey in ((NSArray *)mappedToKey)) {
if (![oneKey isKindOfClass:[NSString class]]) continue;
if (oneKey.length == ) continue;
// 如果映射的是数组,保存 数组到mappedToKeyArray 中, 否则保存 映射字符串
NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
if (keyPath.count > ) {
[mappedToKeyArray addObject:keyPath];
} else {
[mappedToKeyArray addObject:oneKey];
}
// 赋值
if (!propertyMeta->_mappedToKey) {
propertyMeta->_mappedToKey = oneKey;
propertyMeta->_mappedToKeyPath = keyPath.count > ? keyPath : nil;
}
}
if (!propertyMeta->_mappedToKey) return;
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
propertyMeta->_mappedToKey = name;
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
if (mapper.count) _mapper = mapper;
if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
NSLog(@" allmapper: -----%@ \n keyPathPropertyMetas: ------%@",allPropertyMetas,keyPathPropertyMetas);
_classInfo = classInfo;
_keyMappedCount = _allPropertyMetas.count;
_nsType = YYClassGetNSType(cls);
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
return self;
}
下边的方法是 在缓存中读取 抽象类
/// Returns the cached model class meta
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), , &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create();
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
if (!meta || meta->_classInfo.needUpdate) {
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}
简单总结下_YYModelMeta 实现的思路
1.根据类 生成 抽象的ClassInfo 类
classInfo
2. 获取黑名单,在转换过程中会忽略数组中属性
blacklist
3. 获取白名单,转换过程 中处理 数组内的属性,不处理数组外的数据
whitelist
4.对实现了modelContainerPropertyGenericClass 方法 进行必要的转换 类中包含有容易的情况
/**
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"shadows" : [Shadow class],
@"borders" : Border.class,
@"attachments" : @"Attachment" };
}
经过下边转换后得到:
@{
@"shadows" : Shadow,
@"borders" : Border,
@"attachments" : Attachment
};
*/
5. 获取 所有的属性
allPropertyMetas
/**
* 向上层便利类,知道父类为空位置,目的是获取所有的属性
*/
6. 给本类的属性_allPropertyMetas 赋值
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
7. 如果实现了 modelCustomPropertyMapper 方法 也就是自定义了映射
7.1 通过下边的方法可判断是不是进行了自定义映射
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)])
7.2 获取自定义的字典 customMapper
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
7.3 遍历 字典
mappedToKey 有两个类型
一种是:字符串, 另一种是 字符数组
如果是字符串 propertyMeta->_mappedToKey = mappedToKey 直接赋值
如果是数组 取数组中第一个不为空的字符串
同理,keypath 也同上一样的获取到
multiKeysPropertyMetas 同在 在是数组的情况加 添加抽象类
8. 赋值其他
YYModel 源码解读(二)之NSObject+YYModel.h (2)的更多相关文章
- YYModel 源码解读(二)之NSObject+YYModel.h (1)
本篇文章主要介绍 _YYModelPropertyMeta 前边的内容 首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 ...
- jQuery.Callbacks 源码解读二
一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...
- (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin
转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...
- YYModel 源码解读(二)之YYClassInfo.h (3)
前边3篇介绍了YYClassinfo 文件的组成单元,算是功能的分割,按照业务的设计思想来说,方向应该是相反的 由此引申出我们在设计api的思想其实和项目管理是很类似的----- 一些题外话 1.目的 ...
- YYModel 源码解读 总结
在使用swfit写代码的过程中,使用了下oc写的字典转模型,发现有些属性转不成功,就萌生了阅读源码的想法. 其实一直都知道Runtime机制,但并没有系统的学习,可能是因为平时的使用比较少,无意间在g ...
- YYModel 源码解读(一)之YYModel.h
#if __has_include(<YYModel/YYModel.h>) FOUNDATION_EXPORT double YYModelVersionNumber; FOUNDATI ...
- mybatis源码解读(二)——构建Configuration对象
Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...
- ConcurrentHashMap源码解读二
接下来就讲解put里面的三个方法,分别是 1.数组初始化方法initTable() 2.线程协助扩容方法helpTransfer() 3.计数方法addCount() 首先是数组初始化,再将源码之前, ...
- go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin
nsqlookupd: 官方文档解释见:http://bitly.github.io/nsq/components/nsqlookupd.html 用官方话来讲是:nsqlookupd管理拓扑信息,客 ...
- vue2.0 源码解读(二)
小伞最近比较忙,阅读源码的速度越来越慢了 最近和朋友交流的时候,发现他们对于源码的目录结构都不是很清楚 红色圈子内是我们需要关心的地方 compiler 模板编译部分 core 核心实现部分 ent ...
随机推荐
- CORS详解[译]
介绍 由于同源策略的缘故,以往我们跨域请求,会使用诸如JSON-P(不安全)或者代理(设置代理和维护繁琐)的方式.而跨源资源共享(Cross-Origin Resource Sharing)是一个W3 ...
- C语言 · 数位分离
问题描述 编写一个程序,输入一个1000 以内的正整数,然后把这个整数的每一位数字都分离出来,并逐一地显示. 输入格式:输入只有一行,即一个1000以内的正整数. 输出格式:输出只有一行,即该整数的每 ...
- Partition:Partiton Scheme是否指定Next Used?
在SQL Server中,为Partition Scheme多次指定Next Used,不会出错,最后一次指定的FileGroup是Partition Scheme的Next Used,建议,在执行P ...
- 神马玩意,EntityFramework Core 1.1又更新了?走,赶紧去围观
前言 哦,不搞SQL了么,当然会继续,周末会继续更新,估计写完还得几十篇,但是我会坚持把SQL更新完毕,绝不会烂尾,后续很长一段时间没更新的话,不要想我,那说明我是学习新的技能去了,那就是学习英语,本 ...
- Linux上如何查看物理CPU个数,核数,线程数
首先,看看什么是超线程概念 超线程技术就是利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的 ...
- GPG终极指南(加密/签名)
我们平时都听过非对称加密,公钥和私钥,签名验证,但这些证书都是怎么得到的呢?本篇文章会解答这些问题. 背景介绍 加密的一个简单但又实用的任务就是发送加密电子邮件.多年来,为电子邮件进行加密的标准一直是 ...
- WebGIS中等值面展示的相关方案简析
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 等值面是气象.环保等相关项目上常用到的效果展示.在传统的CS项 ...
- 简记用ArcGIS处理某项目需求中数据的步骤
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 项目需求涉及如下几个步骤: a.矢量化 b.获取范围内要素 ...
- 记录一则Linux SSH的互信配置过程
需求:四台Linux主机,IP地址为192.168.10.10/11/12/13,配置登录用户的互信 1.各节点ssh-keygen生成RSA密钥和公钥 ssh-keygen -q -t rsa -N ...
- BPM配置故事之案例3-参与者与数据自动加载
这才过了两天,阿海又来了. 阿海:公司决定改进管理方式,以后物资申请的申请人和申请部门要写具体使用人的名字和部门了. 小明:不是要让我改回去吧? 阿海:那太麻烦了,你能不能把申请人改成选择,选好人自动 ...