Github地址

看到JSON模型转换库,相信大家都没兴趣了,因为各自项目里肯定早都有了。其实也不是我想重复造轮子,主要是这个库其实早在2012年这个库就有了,而那时既没有小码哥的MJExtension,也没有牛逼闪闪的YYModel。那时的JSON模型转换库没有几个,而且都又大又难用,所以我当时自己写了一个。这个库在公司的项目中一直沿用至今,中间我陆续做了点扩展,也优化了一下性能,整体还是非常稳定可用的。

今天突然想把它拿出来,给大家分享一下。因为这个库虽然不是性能最强最全面的,但是它足够的简单,直白,只有两个文件,300行代码,可供初学者学习,也可用大家自行扩展满足自己的需求。

特性

  • 极简,没有复杂的调用,只有300行代码。
  • 市面上其他库支持的转换,这个基本上都有了。
  • Swift下也可以使用。
  • 性能优于MJExtention。

使用

和其他库基本上是差不多的。例子可以参考main.m。

Swift下使用

这个库本身是用OC写的,如果是纯Swift项目就不建议用了。但是对于OC和Swift混编项目,尤其是从OC转过来的项目,还是很好用的。因为OC下的转换通常只要一行代码,项目切换到Swift以后,如果用SwiftyJSON还是挺不习惯的。用这个库可以让OC和Swift里的开发习惯保持基本一致。

Swift下的示例可以参考SwiftJsonTest.swift。 具体有如下几点要求:

  • 模型类需要继承自NSObject,并用@objcMembers声明。
  • 枚举需要继承自Int,并用@objc声明。
  • Int等基础数据类型属性不能声明为可选,需要赋初始值。
  • 枚举属性不能声明为可选,需要赋初始值。
  • 数组属性如果包含自定义类型的元素,映射的时候元素类名前要加上模块名(App名或库名)

代码示例如下:

@objc enum OrderState: Int {case created = 1case completed = 2case canceled = 3
}@objcMembers class OrderInfo: NSObject, JSONEx {var state: OrderState = .createdvar ID: String?var count: Int = 0var product: ProductInfo?static func customPropertyNameForKeys() -> [String : String] {return ["ID": "id"]}
}@objcMembers class School: NSObject, JSONEx {var address: String?var students: [Student] = []static func arrayPropertyItemClasses() -> [String : String] {return ["students": "JSONEx.Student"]}
}
复制代码

调用的时候一句就可以了:

let obj: PhoneInfo = PhoneInfo(dictionary: dic)
复制代码

和OC下的使用是基本一致的。

实现

整体的实现都非常简单直白,只有字典NSDictionary给对象属性赋值的部分代码稍微多一点,这个赋值是基于KVC做的,如果属性有setter方法的话KVC的效率是不差的。对象的基类属性的赋值是通过递归做的。 主要的方法如下:

@implementation NSDictionary (NSDictionary_ObjectMapping)- (void)setObject:(id)obj fromClass:(Class)cls
{unsigned int count = 0;objc_property_t *properties = class_copyPropertyList(cls, &count); //获取该类的所有属性for (int i = 0; i < count; i++) {objc_property_t property = properties[i];const char* key = property_getName(property);NSString* strKey = [NSString stringWithCString:key encoding:NSUTF8StringEncoding];id value = [self objectForKey:strKey];//从字典里按属性名获取valueif (value == nil) {//对象属性名和JSON关键词key不一致的情况,获取属性名对应的keyNSString *jsonKey = [self getObjCustomPropertyKey:obj properyName:strKey];if (jsonKey != nil) {value = [self objectForKey:jsonKey];//再次从字典里获取value}}Class propertyCls = [self getPropertyClass:property];//获取属性的类if (propertyCls == nil || [self isFoundationClass:propertyCls]) {if(value != nil && value != (id)kCFNull) {if (propertyCls == [NSURL class] && [value isKindOfClass:[NSString class]]) {[obj setValue:[NSURL URLWithString:value] forKey:strKey];} else if (propertyCls == [NSArray class] && [value isKindOfClass:[NSArray class]]) {//数组属性里的元素是自定义类型Class cls = [self getObjArrayPropertyClass:obj properyName:strKey];if (cls) {[obj setValue:[cls objectArrayWithArray:value] forKey:strKey];} else {[obj setValue:value forKey:strKey];}} else {[obj setValue:value forKey:strKey];//通过KVC给对象的属性赋值}}} else {//自定义类型属性if(value != nil && [value isKindOfClass:[NSDictionary class]]) {id subObj = [[propertyCls alloc] init];[value setObject:subObj fromAllClass:propertyCls];[obj setValue:subObj forKey:strKey];}}}free(properties);
}@end
复制代码

步骤如下:

  1. 通过class_copyPropertyList获取类的属性列表
  2. 从字典里按属性名获取value
  3. 如果未获取到,则查看该属性名是否设置了对应的Key
  4. 如果设置了则再次从字典里获取value
  5. 获取属性的类型Class
  6. 判断属性是否是自定义的类型
  7. 如果是,那么递归调用该方法给该属性的属性赋值
  8. 如果是基础数据类型,则直接通过KVC给该属性赋值
  9. 如果是数组,则查看是否指定了该数据元素的类型
  10. 指定了则调用JSON数组转对象数组的方法

性能优化

主要优化了两个地方,一是获取属性类型的地方,而是访问扩展设置方法的地方。

- (Class)getPropertyClass:(objc_property_t)property
{unsigned int attrCount;objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);for (unsigned int i = 0; i < attrCount; i++) {const char *name = attrs[i].name;const char *value = attrs[i].value;if (name[0] == 'T') {if (strlen(value) > 2 && value[0] == '@' && value[1] == '\"') {char *p = strrchr(value + 2, '\"');if (p) {*p = '\0';free(attrs);return objc_getClass(value + 2);}}break;}}free(attrs);return nil;
}
复制代码

这里直接对char字符串操作,效率高很多,之前是先转成NSString对象的。关于属性的信息大家可以查这里Property Attribute Description Examples。 对象是T@开头,后面接着的就是类型。

还有一个优化是关于扩展设置方法的:

@protocol JSONEx <NSObject>
@optional
+ (NSDictionary<NSString *, NSString *> *)arrayPropertyItemClasses;//数组属性里元素的类型
+ (NSDictionary<NSString *, NSString *> *)customPropertyNameForKeys;//属性名和JSON关键词的映射
@end
复制代码

每次都读取这两个方法很耗时。我参考YYModel的做法把这两个方法返回的设置一次性读到一个全局的静态字典里,再来访问这个静态变量,速度就快了。代码如下:

// 用静态字典存取类的customPropertyNameForKeys方法的值,以避免函数调用,提高访问速度
static NSDictionary* customPropertyKeyForClass(Class cls) {if (!cls || ![cls respondsToSelector:@selector(customPropertyNameForKeys)]) return nil;static CFMutableDictionaryRef cache;static dispatch_once_t onceToken;static dispatch_semaphore_t lock;dispatch_once(&onceToken, ^{cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);lock = dispatch_semaphore_create(1);});dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);NSDictionary *dic = CFDictionaryGetValue(cache, (__bridge const void *)(cls));dispatch_semaphore_signal(lock);if (!dic) {dic = [(id<JSONEx>)cls customPropertyNameForKeys];if (dic) {dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(dic));dispatch_semaphore_signal(lock);}}return dic;
}
复制代码

这里用GCD的信号量保证多线程情况下的静态字典的读写互斥。

最终的结果: 用YYModel里的用例,循环解析10000遍,在iPhone 7模拟器上,耗时大幅优于MJExtention。

JSONEx: 640.83ms
YYModel: 176.11ms
MJExtension: 2196.75ms

蝇量级的JSON模型转换库(OC,Swift通用)相关推荐

  1. 一个“蝇量级” C 语言协程库

    协程(coroutine)顾名思义就是"协作的例程"(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻 ...

  2. 【Flutter】JSON 模型转换 ( JSON 序列化工具 | JSON 手动序列化 | 根据 JSON 编写 Dart 模型类 | 在线自动根据 JSON 转换 Dart 类 )

    文章目录 一.JSON 序列化工具 二.JSON 手动序列化 三.根据 JSON 编写 Dart 模型类 四.在线自动转换 五.相关资源 一.JSON 序列化工具 JSON 格式比较简单的话 , 使用 ...

  3. Swift JSON与模型转换(HandyJSON封装)

    2018.05.02 20:20:16字数 1,493阅读 5,017 一 简介 二 特性 三 安装使用以及封装 四 使用示例 五 总结 一 简介 HandyJSON是一个用于Swift语言中的JSO ...

  4. 【嵌入式模型转换】1. 算能盒子SE5 芯片板子BM1684

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 1.环境准备 2. 开发机安装环境 编译FP32 BMODEL 转换一个yolov5 tag6.1 执行sophon- ...

  5. 轻松入门模型转换和可视化

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 本文给大家介绍一个模型转换格式ONNX和可视化工具Netron.ONNX是微软设计的一种多平台的通用文 ...

  6. 基于 OData 模型和 JSON 模型的 SAP UI5 表格控件行项目的添加和删除实现

    这是 Jerry 2021 年的第 62 篇文章,也是汪子熙公众号总共第 339 篇原创文章. 龟虽寿曹操神龟虽寿,犹有竟时:腾蛇乘雾,终为土灰.老骥伏枥,志在千里:烈士暮年,壮心不已.盈缩之期,不但 ...

  7. JavaScript玩转机器学习:模型转换

    JavaScript玩转机器学习:模型转换 模型转换 TensorFlow.js 配备了各种预训练模型,这些模型可以在浏览器中使用,模型仓库 中有相关介绍.但是,您可能已经在其他地方找到或创建了一个 ...

  8. TF-Lite极简参考-模型转换

    TF-Lite极简参考-模型转换 <TF-Lite极简参考-模型转换>   TensorFlow Lite 可以很方便的把基于TensorFlow训练的模型进行转换,然后推理,在Tenso ...

  9. NNIE模型转换环境搭建

    NNIE模型转换环境搭建 <NNIE模型转换环境搭建>   推荐使用开源项目 https://github.com/RaySue/NNIE-lite ,使用NNIE像使用ncnn一样简单. ...

  10. 模型转换、压缩、加速工具

    20210618 sky_hole: 回成都工作了吗?wang shi yang: 嗯 我现在已经在成都上班了sky_hole: 不用付费,我之前发你的视频你好好看看就可以入门了sky_hole: 成 ...

最新文章

  1. 移动端实现文字轮播_使用原生JS实现移动端图片轮播效果(一)
  2. 常见Z纯CSS小样式合集(三角形)
  3. jsonp react 获取返回值_谈谈对 React 新旧生命周期的理解
  4. BZOJ solve 100 纪念
  5. 路由器与交换机的区别【知识普及】
  6. mybatis if标签字符串判断
  7. 了解更多关于11gR2 diskmon
  8. 使用监听器实现实时在线人数统计
  9. Archlinux下firefox无法使用支付宝控件(aliedit)输入密码/进行支付的解决办法
  10. Cosmos 是什么? 一文了解Cosmos的来龙去脉
  11. 关于信号发生器的功能和参数介绍(二)
  12. 清华本科结业生两年的工作经历-献给游走在黑暗里的清华人
  13. 网络—— 数据链路层,MTU
  14. 辛钦大数定理(揭示了均值和数学期望的关系)
  15. 抽象类 与 委托 蛋蛋的忧桑
  16. Jetson Nano从零开始(2):硬件篇
  17. C++ sort 排序函数使用方法
  18. 咸鱼带你学计算机网络—概论
  19. 机械硬盘选购指南——从选购经历谈起
  20. SQL-高级命令(一)语句:like通配符,as别名

热门文章

  1. cubieboard2 android,cubieboard2双卡版系统安装指南_android.pdf
  2. 关于虚拟机非正常关机的解决方案
  3. 一、Oracle创建账户、修改、删除账户及授权和撤销授权。
  4. 家用千兆路由器排行榜前十名_公认最好的路由器牌子
  5. 傅里叶分析之掐死教程(完整版)(转)
  6. 巧用立体声codec CL1026 的EQ
  7. Region Proposal Networks 详解
  8. net新的库相关的资源
  9. 学习Unity3D是一件痛并快乐着的事
  10. 科学道德与学风-2021雨课堂答案-第10章