YYModel 源码分析:模型转字典
YYModel , 模型字典,自动转化,
模型转字典, 主要是通过运行时,把模型的属性名取出来,递归构建字典
YYModel 设计 4 个模型,记录类与其属性信息,
类 + 信息记录
类
属性 + 信息记录
属性
两个用于类,两个用于属性
如果从上到下,逐个包含,就很清晰
YYModel 为了方便逻辑处理,四个模型包含交叉,有点绕
本文通过模型转字典,看下其运转
调用:
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; Book * page = [Book yy_modelWithJSON: jsonData];NSDictionary * dict = [page yy_modelToJSONObject];
实现:
- (id)yy_modelToJSONObject {// 取出 jsonid jsonObject = ModelToJSONObjectRecursive(self);// 判断数组if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;// 判断字典if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;return nil;
}
对模型,递归处理
// 拿到模型,算出字典
static id ModelToJSONObjectRecursive(NSObject *model) {
// 不存在,就返回if (!model || model == (id)kCFNull) return model;// 抵达终端的值if ([model isKindOfClass:[NSString class]]) return model;if ([model isKindOfClass:[NSNumber class]]) return model;// ...// 处理字典// 处理集合 Set// 处理数组if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;if ([model isKindOfClass:[NSDate class]]) return @"ha ha";if ([model isKindOfClass:[NSData class]]) return nil;_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];if (!modelMeta) return nil;// 建立字典NSMutableDictionary *result = [[NSMutableDictionary alloc] init];__unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {if (!propertyMeta->_getter) return;// 获取值id value = nil;if (propertyMeta->_isCNumber) {value = ModelCreateNumberFromProperty(model, propertyMeta);} else if (propertyMeta->_nsTypeX) {// _nsTypeX 是苹果默认对象id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);value = ModelToJSONObjectRecursive(v);} else {switch (propertyMeta->_typeA & YYEncodingTypeMask) {case YYEncodingTypeObject: {// 处理自定义对象id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);// 对于对象,递归处理value = ModelToJSONObjectRecursive(v);if (value == (id)kCFNull) value = nil;} break;case YYEncodingTypeClass: {Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);value = v ? NSStringFromClass(v) : nil;} break;case YYEncodingTypeSEL: {SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);value = v ? NSStringFromSelector(v) : nil;} break;default: break;}}if (!value) return;if (!dic[propertyMeta->_mappedToKey]) {// 赋值dic[propertyMeta->_mappedToKey] = value;}}];return result;
}
其中,
if (propertyMeta->_nsTypeX)
如果 _nsTypeX
不存在,就是 _nsTypeX
= 0, YYEncodingTypeNSUnknown
结合下面代码,
如果 _nsTypeX
是 NSString
、NSNumber
、NSValue
、NSData
、NSArray
、NSDictionary
, 就递归处理掉
static YYEncodingNSType YYClassGetNSType(Class cls){if (!cls) return YYEncodingTypeNSUnknown;if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;return YYEncodingTypeNSUnknown;
}
runtime:
@interface NSObject (Model)
给 NSObject
加扩展,model 可以直接调用
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic
方法中,
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
@interface _YYModelMeta: NSObject
类信息,并添加自定义信息
先类方法,再实例方法
类方法加锁,做缓存
实例方法,处理属性
+ (instancetype)metaWithClass:(Class)cls
方法中,
meta = [[_YYModelMeta alloc] initWithClass:cls];
- (instancetype)initWithClass:(Class)cls
方法中,
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
@interface YYClassInfo : NSObject
类信息
先类方法,再实例方法
类方法加锁,做缓存
实例方法,处理属性
+ (instancetype)classInfoWithClass:(Class)cls
方法中,
info = [[YYClassInfo alloc] initWithClass:cls];
- (instancetype)initWithClass:(Class)cls
方法中,
- (instancetype)initWithClass:(Class)cls {// ...unsigned int propertyCount = 0;// 从类取出属性objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);if (properties) {NSMutableDictionary *propertyInfos = [NSMutableDictionary new];_propertyInfos = propertyInfos;for (unsigned int i = 0; i < propertyCount; i++) {// 实例化,每一个属性YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];if (info.name) propertyInfos[info.name] = info;}free(properties);}// ...return self;
}
@interface YYClassPropertyInfo : NSObject
属性信息
- (instancetype)initWithProperty:(objc_property_t)property {if (!property) return nil;self = [super init];const char *name = property_getName(property);if (name) {// 获取属性名,做了一个 UTF8 编码_name = [NSString stringWithUTF8String:name];}YYEncodingType type = 0;unsigned int attrCount;// 从属性中,翻出所有的信息objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);for (unsigned int i = 0; i < attrCount; i++) {// 处理属性中,每一条信息switch (attrs[i].name[0]) {case 'T': { // Type encodingif (attrs[i].value) {// 拿到编码方式_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];type = YYEncodingGetType(attrs[i].value);if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];if (![scanner scanString:@"@\"" intoString:NULL]) continue;NSString *clsName = nil;if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {// 拿到类信息if (clsName.length) _clsX = objc_getClass(clsName.UTF8String);}// _protocols// ...}}} break;// ...}}// ...return self;
}
@interface _YYModelPropertyMeta: NSObject
属性信息,和属性的添加类中
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {// ..._YYModelPropertyMeta *meta = [self new];// 把上一步,获取的属性信息,拷贝出来meta->_name = propertyInfo.name;meta->_typeA = propertyInfo.type;meta->_info = propertyInfo;// ...// 获取类型信息if ((meta->_typeA & YYEncodingTypeMask) == YYEncodingTypeObject) {meta->_nsTypeX = YYClassGetNSType(propertyInfo.clsX);} else {meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_typeA);}
模型转字典部分,通过记录的属性类型,处理
是数字,直接处理
非数字,系统类型优先处理
最后的情况下,处理对象等
相关
YYModel 源码分析:模型转字典相关推荐
- YYModel 源码分析:模型设计
前文介绍了 YYModel 怎么字典转模型, YYModel 源码分析:字典转模型 概述 本文继续探讨,modelMeta,存放模型的属性.和字典的键对应关系的描述文件 结构: 主要使用四个模型: @ ...
- YYModel 源码分析:字典转模型
本文拿一个简单的例子,看 YYModel 字典转模型的源代码 有这么个模型 @interface Author : NSObject @property NSString *name; @proper ...
- Redis 数据结构-字典源码分析
2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...
- YYImage实现思路源码分析(图片解压缩原理)
YYKit组件之一---->YYImage 图像处理 移动端图片格式调研 图片处理的小技巧 YYWebImage源码分析 YYModel源码分析 YYText源码分析 核心思路--->图片 ...
- nlu模型训练源码分析
nlu模型训练源码分析 rasa/train.py是模型训练的文件,_train_async_internal函数是训练nlu和core的入口,_train_nlu_with_validated_da ...
- 阿里面试这样问:Nacos配置中心交互模型是 push 还是 pull ?(原理+源码分析)...
本文来源:公众号「 程序员内点事」 对于Nacos大家应该都不太陌生,出身阿里名声在外,能做动态服务发现.配置管理,非常好用的一个工具.然而这样的技术用的人越多面试被问的概率也就越大,如果只停留在使用 ...
- Spark源码分析之九:内存管理模型
Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Sp ...
- Spark源码分析之二:Job的调度模型与运行反馈
在<Spark源码分析之Job提交运行总流程概述>一文中,我们提到了,Job提交与运行的第一阶段Stage划分与提交,可以分为三个阶段: 1.Job的调度模型与运行反馈: 2.Stage划 ...
- [源码分析] Facebook如何训练超大模型 --- (3)
[源码分析] Facebook如何训练超大模型 - (3) 文章目录 [源码分析] Facebook如何训练超大模型 --- (3) 0x00 摘要 0x01 ZeRO-Offload 1.1 设计原 ...
最新文章
- Manifest merger failed Suggestion: add 'tools:replace=“Android:value”' to meta-data element at And
- 原版销售累计超过150 000册的经典JavaScript入门书
- 参加51CTO学院软考培训,通过后感想
- OpenCV调整彩色图像的饱和度和亮度
- Ardino基础教程 8_模拟值
- java高级----Java动态代理的原理
- spring框架 web开发_go语言web开发框架:Iris框架讲解(一)
- 【Linux】Linux用户、用户组、文件权限学习笔记
- Siki_Unity_2-5_DOTween动画插件(未学)
- 除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效。...
- 为何大部分人成不了技术专家?
- 1.10 梯度消失与梯度爆炸
- ODE手册(6)关节类型和相关函数(下)
- Java 阻塞队列实现原理分析
- 在Windows下配置Ubuntu启动引导项
- 如何通过微信小程序进行更加有效的电商营销?
- xmind怎么导出甘特图_张兵导图:xmind如何绘制甘特图?
- java 爬取快递100 快递信息
- 申请美国计算机专业,美国计算机专业申请人数过八万
- wasp软件怎么安装不了_【NodeJS】安装
热门文章
前文介绍了 YYModel 怎么字典转模型, YYModel 源码分析:字典转模型 概述 本文继续探讨,modelMeta,存放模型的属性.和字典的键对应关系的描述文件 结构: 主要使用四个模型: @ ...
本文拿一个简单的例子,看 YYModel 字典转模型的源代码 有这么个模型 @interface Author : NSObject @property NSString *name; @proper ...
2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...
YYKit组件之一---->YYImage 图像处理 移动端图片格式调研 图片处理的小技巧 YYWebImage源码分析 YYModel源码分析 YYText源码分析 核心思路--->图片 ...
nlu模型训练源码分析 rasa/train.py是模型训练的文件,_train_async_internal函数是训练nlu和core的入口,_train_nlu_with_validated_da ...
本文来源:公众号「 程序员内点事」 对于Nacos大家应该都不太陌生,出身阿里名声在外,能做动态服务发现.配置管理,非常好用的一个工具.然而这样的技术用的人越多面试被问的概率也就越大,如果只停留在使用 ...
Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Sp ...
在<Spark源码分析之Job提交运行总流程概述>一文中,我们提到了,Job提交与运行的第一阶段Stage划分与提交,可以分为三个阶段: 1.Job的调度模型与运行反馈: 2.Stage划 ...
[源码分析] Facebook如何训练超大模型 - (3) 文章目录 [源码分析] Facebook如何训练超大模型 --- (3) 0x00 摘要 0x01 ZeRO-Offload 1.1 设计原 ...