_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)的更多相关文章

  1. YYModel 源码解读(二)之NSObject+YYModel.h (1)

    本篇文章主要介绍 _YYModelPropertyMeta 前边的内容 首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 ...

  2. jQuery.Callbacks 源码解读二

    一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...

  3. (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

    转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...

  4. YYModel 源码解读(二)之YYClassInfo.h (3)

    前边3篇介绍了YYClassinfo 文件的组成单元,算是功能的分割,按照业务的设计思想来说,方向应该是相反的 由此引申出我们在设计api的思想其实和项目管理是很类似的----- 一些题外话 1.目的 ...

  5. YYModel 源码解读 总结

    在使用swfit写代码的过程中,使用了下oc写的字典转模型,发现有些属性转不成功,就萌生了阅读源码的想法. 其实一直都知道Runtime机制,但并没有系统的学习,可能是因为平时的使用比较少,无意间在g ...

  6. YYModel 源码解读(一)之YYModel.h

    #if __has_include(<YYModel/YYModel.h>) FOUNDATION_EXPORT double YYModelVersionNumber; FOUNDATI ...

  7. mybatis源码解读(二)——构建Configuration对象

    Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...

  8. ConcurrentHashMap源码解读二

    接下来就讲解put里面的三个方法,分别是 1.数组初始化方法initTable() 2.线程协助扩容方法helpTransfer() 3.计数方法addCount() 首先是数组初始化,再将源码之前, ...

  9. go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

    nsqlookupd: 官方文档解释见:http://bitly.github.io/nsq/components/nsqlookupd.html 用官方话来讲是:nsqlookupd管理拓扑信息,客 ...

  10. vue2.0 源码解读(二)

    小伞最近比较忙,阅读源码的速度越来越慢了 最近和朋友交流的时候,发现他们对于源码的目录结构都不是很清楚 红色圈子内是我们需要关心的地方 compiler  模板编译部分 core 核心实现部分 ent ...

随机推荐

  1. Fis3的前端工程化之路[三大特性篇之声明依赖]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  2. 一步步开发自己的博客 .NET版(11、Web.config文件的读取和修改)

    Web.config的读取 对于Web.config的读取大家都很属性了.平时我们用得比较多的就是appSettings节点下配置.如: 我们对应的代码是: = ConfigurationManage ...

  3. 线性数据结构之栈——Stack

    Linear data structures linear structures can be thought of as having two ends, whose items are order ...

  4. Modify Branding of FreeCAD

    Modify Branding of FreeCAD eryar@163.com This article describes the Branding of FreeCAD. Branding me ...

  5. 前端学HTTP之报文首部

    前面的话 首部和方法配合工作,共同决定了客户端和服务器能做什么事情.在请求和响应报文中都可以用首部来提供信息,有些首部是某种报文专用的,有些首部则更通用一些.本文将详细介绍HTTP报文中的首部 结构 ...

  6. ASP.NET Core 1.0 开发记录

    官方资料: https://github.com/dotnet/core https://docs.microsoft.com/en-us/aspnet/core https://docs.micro ...

  7. ZKWeb网页框架1.1正式发布

    发行日志 https://github.com/zkweb-framework/ZKWeb/blob/master/ReleaseNotes/ReleaseNote.1.1.md 主要改动 添加EFC ...

  8. 【Linux大系】Linux的概念与体系

    感谢原作者:Vamei 出处:http://www.cnblogs.com/vamei 我在这一系列文章中阐述Linux的基 本概念.Linux操作系统继承自UNIX.一个操作系统是一套控制和使用计算 ...

  9. Lind.DDD.LindMQ~关于持久化到Redis的消息格式

    回到目录 关于持久化到Redis的消息格式,主要是说在Broker上把消息持久化的过程中,需要存储哪些类型的消息,因为我们的消息是分topic的,而每个topic又有若干个queue组成,而我们的to ...

  10. H3 BPM引擎API接口

    引擎API接口通过 Engine 对象进行访问,这个是唯一入口. 示例1:获取组织机构对象 this.Engine.Organization.GetUnit("组织ID"); 示例 ...