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

结合下面代码,

如果 _nsTypeXNSStringNSNumberNSValueNSDataNSArrayNSDictionary, 就递归处理掉


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 源码分析:模型转字典相关推荐

  1. YYModel 源码分析:模型设计

    前文介绍了 YYModel 怎么字典转模型, YYModel 源码分析:字典转模型 概述 本文继续探讨,modelMeta,存放模型的属性.和字典的键对应关系的描述文件 结构: 主要使用四个模型: @ ...

  2. YYModel 源码分析:字典转模型

    本文拿一个简单的例子,看 YYModel 字典转模型的源代码 有这么个模型 @interface Author : NSObject @property NSString *name; @proper ...

  3. Redis 数据结构-字典源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...

  4. YYImage实现思路源码分析(图片解压缩原理)

    YYKit组件之一---->YYImage 图像处理 移动端图片格式调研 图片处理的小技巧 YYWebImage源码分析 YYModel源码分析 YYText源码分析 核心思路--->图片 ...

  5. nlu模型训练源码分析

    nlu模型训练源码分析 rasa/train.py是模型训练的文件,_train_async_internal函数是训练nlu和core的入口,_train_nlu_with_validated_da ...

  6. 阿里面试这样问:Nacos配置中心交互模型是 push 还是 pull ?(原理+源码分析)...

    本文来源:公众号「 程序员内点事」 对于Nacos大家应该都不太陌生,出身阿里名声在外,能做动态服务发现.配置管理,非常好用的一个工具.然而这样的技术用的人越多面试被问的概率也就越大,如果只停留在使用 ...

  7. Spark源码分析之九:内存管理模型

    Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Sp ...

  8. Spark源码分析之二:Job的调度模型与运行反馈

    在<Spark源码分析之Job提交运行总流程概述>一文中,我们提到了,Job提交与运行的第一阶段Stage划分与提交,可以分为三个阶段: 1.Job的调度模型与运行反馈: 2.Stage划 ...

  9. [源码分析] Facebook如何训练超大模型 --- (3)

    [源码分析] Facebook如何训练超大模型 - (3) 文章目录 [源码分析] Facebook如何训练超大模型 --- (3) 0x00 摘要 0x01 ZeRO-Offload 1.1 设计原 ...

最新文章

  1. Manifest merger failed Suggestion: add 'tools:replace=“Android:value”' to meta-data element at And
  2. 原版销售累计超过150 000册的经典JavaScript入门书
  3. 参加51CTO学院软考培训,通过后感想
  4. OpenCV调整彩色图像的饱和度和亮度
  5. Ardino基础教程 8_模拟值
  6. java高级----Java动态代理的原理
  7. spring框架 web开发_go语言web开发框架:Iris框架讲解(一)
  8. 【Linux】Linux用户、用户组、文件权限学习笔记
  9. Siki_Unity_2-5_DOTween动画插件(未学)
  10. 除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效。...
  11. 为何大部分人成不了技术专家?
  12. 1.10 梯度消失与梯度爆炸
  13. ODE手册(6)关节类型和相关函数(下)
  14. Java 阻塞队列实现原理分析
  15. 在Windows下配置Ubuntu启动引导项
  16. 如何通过微信小程序进行更加有效的电商营销?
  17. xmind怎么导出甘特图_张兵导图:xmind如何绘制甘特图?
  18. java 爬取快递100 快递信息
  19. 申请美国计算机专业,美国计算机专业申请人数过八万
  20. wasp软件怎么安装不了_【NodeJS】安装

热门文章

  1. SpringMVC中出现的线程安全问题分析
  2. 什么是[ MQTT ]协议?
  3. Html页面与页面间的交互
  4. 陀螺产业区块链第五季 | 基于区块链的不动产交易协作平台
  5. Floodlight 用Static Flow Pusher 改变流表规则
  6. linux drwxr-xr-x 的学习
  7. python中值滤波、最大池化、平均池化、canny边缘检测(石原里美系列一)
  8. 全网疯传!java编程代写有哪些平台
  9. 来,设计个微信朋友圈-Feed流
  10. 浅议应用系统“垂直切换”