蝇量级的JSON模型转换库(OC,Swift通用)
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
复制代码
步骤如下:
- 通过
class_copyPropertyList
获取类的属性列表 - 从字典里按属性名获取value
- 如果未获取到,则查看该属性名是否设置了对应的Key
- 如果设置了则再次从字典里获取value
- 获取属性的类型
Class
- 判断属性是否是自定义的类型
- 如果是,那么递归调用该方法给该属性的属性赋值
- 如果是基础数据类型,则直接通过KVC给该属性赋值
- 如果是数组,则查看是否指定了该数据元素的类型
- 指定了则调用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通用)相关推荐
- 一个“蝇量级” C 语言协程库
协程(coroutine)顾名思义就是"协作的例程"(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻 ...
- 【Flutter】JSON 模型转换 ( JSON 序列化工具 | JSON 手动序列化 | 根据 JSON 编写 Dart 模型类 | 在线自动根据 JSON 转换 Dart 类 )
文章目录 一.JSON 序列化工具 二.JSON 手动序列化 三.根据 JSON 编写 Dart 模型类 四.在线自动转换 五.相关资源 一.JSON 序列化工具 JSON 格式比较简单的话 , 使用 ...
- Swift JSON与模型转换(HandyJSON封装)
2018.05.02 20:20:16字数 1,493阅读 5,017 一 简介 二 特性 三 安装使用以及封装 四 使用示例 五 总结 一 简介 HandyJSON是一个用于Swift语言中的JSO ...
- 【嵌入式模型转换】1. 算能盒子SE5 芯片板子BM1684
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 1.环境准备 2. 开发机安装环境 编译FP32 BMODEL 转换一个yolov5 tag6.1 执行sophon- ...
- 轻松入门模型转换和可视化
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 本文给大家介绍一个模型转换格式ONNX和可视化工具Netron.ONNX是微软设计的一种多平台的通用文 ...
- 基于 OData 模型和 JSON 模型的 SAP UI5 表格控件行项目的添加和删除实现
这是 Jerry 2021 年的第 62 篇文章,也是汪子熙公众号总共第 339 篇原创文章. 龟虽寿曹操神龟虽寿,犹有竟时:腾蛇乘雾,终为土灰.老骥伏枥,志在千里:烈士暮年,壮心不已.盈缩之期,不但 ...
- JavaScript玩转机器学习:模型转换
JavaScript玩转机器学习:模型转换 模型转换 TensorFlow.js 配备了各种预训练模型,这些模型可以在浏览器中使用,模型仓库 中有相关介绍.但是,您可能已经在其他地方找到或创建了一个 ...
- TF-Lite极简参考-模型转换
TF-Lite极简参考-模型转换 <TF-Lite极简参考-模型转换> TensorFlow Lite 可以很方便的把基于TensorFlow训练的模型进行转换,然后推理,在Tenso ...
- NNIE模型转换环境搭建
NNIE模型转换环境搭建 <NNIE模型转换环境搭建> 推荐使用开源项目 https://github.com/RaySue/NNIE-lite ,使用NNIE像使用ncnn一样简单. ...
- 模型转换、压缩、加速工具
20210618 sky_hole: 回成都工作了吗?wang shi yang: 嗯 我现在已经在成都上班了sky_hole: 不用付费,我之前发你的视频你好好看看就可以入门了sky_hole: 成 ...
最新文章
- 移动端实现文字轮播_使用原生JS实现移动端图片轮播效果(一)
- 常见Z纯CSS小样式合集(三角形)
- jsonp react 获取返回值_谈谈对 React 新旧生命周期的理解
- BZOJ solve 100 纪念
- 路由器与交换机的区别【知识普及】
- mybatis if标签字符串判断
- 了解更多关于11gR2 diskmon
- 使用监听器实现实时在线人数统计
- Archlinux下firefox无法使用支付宝控件(aliedit)输入密码/进行支付的解决办法
- Cosmos 是什么? 一文了解Cosmos的来龙去脉
- 关于信号发生器的功能和参数介绍(二)
- 清华本科结业生两年的工作经历-献给游走在黑暗里的清华人
- 网络—— 数据链路层,MTU
- 辛钦大数定理(揭示了均值和数学期望的关系)
- 抽象类 与 委托 蛋蛋的忧桑
- Jetson Nano从零开始(2):硬件篇
- C++ sort 排序函数使用方法
- 咸鱼带你学计算机网络—概论
- 机械硬盘选购指南——从选购经历谈起
- SQL-高级命令(一)语句:like通配符,as别名