iOS 开发中总会用到各种 JSON 模型转换库,这篇文章将会对常见的几个开源库进行一下评测。评测的内容主要集中在性能、功能、容错性这几个方面。
评测的对象:
Manually
手动进行 JSON/Model 转换,不用任何开源库,可以进行高效、自由的转换,但手写代码非常繁琐,而且容易出错。
YYModel
我造的一个新轮子,比较轻量(算上 .h 只有 5 个文件),支持自动的 JSON/Model 转换,支持定义映射过程。API 简洁,功能也比较简单。
FastEasyMapping
Yalantis 开发的一个 JSON 模型转换库,可以自定义详细的 Model 映射过程,支持 CoreData。使用者较少。
JSONModel
一个 JSON 模型转换库,有着比较简洁的接口。Model 需要继承自 JSONModel。
Mantle
Github 官方团队开发的 JSON 模型转换库,Model 需要继承自 MTLModel。功能丰富,文档完善,使用广泛。
MJExtension
国内开发者”小码哥”开发的 JSON 模型库,号称性能超过 JSONModel 和 Mantle,使用简单无侵入。国内有大量使用者。
性能评测:
所有开源库代码更新至 2015-09-18,以 Release 方式编译,运行在 iPhone 6 上,代码见 https://github.com/ibireme/YYModel/tree/master/Benchmark。
用例1:GithubUser
从 https://api.github.com/users/facebook 获取的一条 User 数据,去除 NSDate 属性。
该 JSON 有 30 行,大部分属性是 string,少量是 number。这个用例主要是测试最基础的 Model 相关操作。
每次测试执行 10000 次,统计耗时毫秒数。
用例2: WeiboStatus
从官方微博 App 抓取一条内容完整的微博数据,JSON 总共有 580 行(是的,一条微博需要这么大数据量),包含大量嵌套对象、容器对象、类型转换、日期解析等。这个用例主要是测试在复杂的情况下不同库的性能。
每次测试执行 1000 次,统计耗时毫秒数。
测试结果如下:
Mantle 在各个测试中,性能都是最差的
JSONModel 和 MJExtension 性能相差不多,但都比 Mantle 性能高。
FastEasyMapping 相对来说性能确实比较快。
YYModel 性能高出其他几个库一个数量级,接近手写代码的效率。
FastEasyMapping 不支持 NSCoding 协议,所以不能进行 Archive 的性能测试。
MJExtension 在处理复杂对象转为 JSON 时,存在错误。
(此处我也测试了一些 Swift 的项目,例如 ObjectMapper、JSONHelper、ModelRocket,性能比 Mantle 还差很多,这里就不进行对比了。)
容错性:
容错性主要是测试在默认情况下,当 JSON 格式错误时,Model 框架是否会产生错误结果或造成 Crash。
用例 | YYModel | FastEasyMapping | JSONModel | Mantle | MJExtension |
---|---|---|---|---|---|
JSON 属性是 number Model 属性是 NSString |
✅ NSString | ? NSNumber | ✅ NSString | ⚠️ model nil | ✅ NSString |
JSON 属性是 string “100” Model 属性是 int |
✅ 100 | ? Crash | ? Crash | ⚠️ model nil | ✅ 100 |
JSON 属性是 string “2009-04-02T03:35:22Z” Model 属性是 NSDate |
✅ NSDate | ? NSString | ✅ NSDate | ⚠️ model nil | ? NSString |
JSON 属性是 string Model 属性是 NSValue |
✅ nil | ? NSString | ? crash | ⚠️ model nil | ? NSString |
YYModel 和 Mantle 都会进行对象类型检查,避免将错误的对象类型赋值到属性,以避免潜在的 Crash 问题。不同的是 YYModel 会尝试自动转换,转换失败时留空;而 Mantle 遇到类型不匹配时,直接把错误向上返回,从而终止了整个转换过程,但这么做更方便调试。
MJExtension 会对部分对象进行自动转换(比如 NSString 和 NSNumber 之间的转换),但当自动转换不能完成时,它会直接把 JSON 对象赋值给类型不匹配的 Model 属性。这样的结果会导致稍后 Model 在使用时,造成潜在的 Crash 风险。
JSONModel 并没有对错误类型的检测,并且没有对 App 的保护,当出现异常时,会导致整个 App Crash,非常危险。
FastEasyMapping 表现则是最差的,它没有自动转换的机制,当遇到类型不匹配时,会导致错误的类型赋值,甚至直接 Crash。
功能:
功能 | YYModel | FastEasyMapping | JSONModel | Mantle | MJExtension |
---|---|---|---|---|---|
相同属性名自动映射 | ✅ | ? | ✅ | ✅ | ✅ |
自定义属性转换方式 | ? | ✅ | ? | ✅ | ? |
NSCoding | ✅ | ? | ✅ | ✅ | ✅ |
hash/equal | ✅ | ? | ✅ | ✅ | ? |
CoreData | ? | ✅ | ? | ✅ | ✅ |
就功能来说,Mantle 的可定制性最高,功能相对比较丰富。
YYModel、JSONModel、MJExtension 使用比较简单,但功能相对 Mantle 稍弱。
FastEasyMapping 功能最少,使用也不算方便。
侵入性:
Mantle 和 JSONModel 都需要 Model 继承自某个基类,灵活性稍差,但功能丰富。
YYModel、MJExtension 都是采用 Category 方式来实现功能,比较灵活,无侵入。
但注意 MJExtension 为 NSObject/NSString 添加了一些没有前缀的方法,且方法命名比较通用,可能会和一个工程内的其他类有冲突。
FastEasyMapping 采用工具类来实现 Model 转换的功能,最为灵活,但使用很不方便。
结论:
如果需要一个稳定、功能强大的 Model 框架,Mantle 是最佳选择,它唯一的缺点就是性能比较差。
如果对功能要求并不多,但对性能有更高要求时,可以试试我的 YYModel。
Swift 相关的几个库性能都比较差,非 Swift 项目不推荐使用。
最后提一句,如果对性能、网络流量等有更高的要求,就不要再用 JSON 了,建议改用 protobuf/FlatBuffers 这样的方案。JSON 转换再怎么优化,在性能和流量方面还是远差于二进制格式的。
附: YYModel 性能优化的几个 Tip:
1. 缓存
Model JSON 转换过程中需要很多类的元数据,如果数据足够小,则全部缓存到内存中。
2. 查表
当遇到多项选择的条件时,要尽量使用查表法实现,比如 switch/case,C Array,如果查表条件是对象,则可以用 NSDictionary 来实现。
3. 避免 KVC
Key-Value Coding 使用起来非常方便,但性能上要差于直接调用 Getter/Setter,所以如果能避免 KVC 而用 Getter/Setter 代替,性能会有较大提升。
4. 避免 Getter/Setter 调用
如果能直接访问 ivar,则尽量使用 ivar 而不要使用 Getter/Setter 这样也能节省一部分开销。
5. 避免多余的内存管理方法
在 ARC 条件下,默认声明的对象是 __strong 类型的,赋值时有可能会产生 retain/release 调用,如果一个变量在其生命周期内不会被释放,则使用 __unsafe_unretained 会节省很大的开销。
访问具有 __weak 属性的变量时,实际上会调用 objc_loadWeak() 和 objc_storeWeak() 来完成,这也会带来很大的开销,所以要避免使用 __weak 属性。
创建和使用对象时,要尽量避免对象进入 autoreleasepool,以避免额外的资源开销。
6. 遍历容器类时,选择更高效的方法
相对于 Foundation 的方法来说,CoreFoundation 的方法有更高的性能,用 CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 方法来遍历容器类能带来不少性能提升,但代码写起来会非常麻烦。
7. 尽量用纯 C 函数、内联函数
使用纯 C 函数可以避免 ObjC 的消息发送带来的开销。如果 C 函数比较小,使用 inline 可以避免一部分压栈弹栈等函数调用的开销。
8. 减少遍历的循环次数
在 JSON 和 Model 转换前,Model 的属性个数和 JSON 的属性个数都是已知的,这时选择数量较少的那一方进行遍历,会节省很多时间。
你好…请求添加此库 https://github.com/dake/NSObject-TCMapping 性能对比, thank you!
没有想对比的意思..只是略好奇…..
只测了一下 GithubUser JSON 转 Model,耗时大概是 Mantle 的 1.2 倍 ;-)
:!: 知晓了…谢谢哦
你这个是怎么记录这个映射运行速度的?如果数据结构,层级更深,这个会不会对这个分析结果有影响?
谢谢分享,我在新项目里试试YYModel
感谢分享,打算试试 YYModel
:oops:
YYModel 没有添加「自定义属性转换方式」是因为性能的原因吗?
性能倒不是问题。。只是我不想把 API 搞的太复杂了 :shock:
谢谢分享。 希望这个库能越来越强大。
想说下mantle的两点,虽然不是自动支持的。 但
mantle可以相同属性名自动映射。用:mtl_identityPropertyMapWithModel
mantle也支持core data: https://github.com/Mantle/MTLManagedObjectAdapter
感谢指证 :smile: 我会改一下~
有没有swift 此类型框架的推荐
兄台,能不能看下SwiftyJSON的效率怎样?最近自己的项目用了这个.
Swift 的几个库性能都不算高。。
确实, 我之前也有写过Swift的, 差太多, 没好意思放出来。
语言特性吧, 有其优势, 也有所牺牲。
请教:
“JSONModel 并没有对错误类型的检测,并且没有对 App 的保护,当出现异常时,会导致整个 App Crash,非常危险。”
JSONModel没有对错误类型做检测具体是指什么?OC作为一种动态语言,类型安全问题是与生俱来的。“保护”问题我觉得更应该理解为需要保护到什么程度,由谁来做保护措施。“异常”是指出现在JSONModel内部的转换异常还是指App在使用model class时出现类型安全问题的异常?
“在 ARC 条件下,默认声明的对象是 __strong 类型的,赋值时有可能会产生 retain/release 调用,如果一个变量在其生命周期内不会被释放,则使用 __unsafe_unretained 会节省很大的开销…”
楼主的偏好是说用__unsafe_unretained来代替__weak的使用,使用后自行解决野指针的问题吗?
关于类型安全举个例子:Model 中有个属性是 int count,而服务端传过来的 JSON 有问题(或者被运营商劫持插入了什么东西),是个 String 类型的值,这时调用 JSONModel 的方法,JSONModel 会在内部出现异常,这时只能通过外层的 try cache 来保证程序不崩溃。
关于 __unsafe_unretained 这个属性,我只提到需要在性能优化时才需要尝试使用,平时开发自然是不推荐用的。
是,被劫持修改了什么类型结果或者服务器端返回异常都是存在这些可能性的,如果在开发调试中都没发现问题的话,那只能自求多福了。
对于jsonmodel的自定义属性转换,是指自定义属性映射吗?如果是,jsonmodel是支持的
+(JSONKeyMapper*)keyMapper
{
return [[JSONKeyMapper alloc] initWithDictionary:@{
@”order_id”: @”id”,
@”order_details.name”: @”productName”,
@”order_details.price.usd”: @”price”
}];
}
比如定义某个属性如何由 NSString 转为 UIColor 这种
0.感谢写出这么好的文章,特别是后面的tips,很实用,非常感谢
1.YYModel使用Ivar,无法转换CoreData的Dynamic模型属性
2.YYModel的功能对比Mantle、JSONModel、MJExtension是少很多的,这些老框架功能强大了,肯定会变重,性能肯定会有所下降的。现在的性能比较,个人感觉都不是最客观的
3.以个人之见去比较第三方框架是可以的,但是说不推荐使用JSONModel等框架,反而推荐使用自己的框架,这是损人不利己的做法。究竟用哪个框架,大家看完你的测评后,自然会有个选择,萝卜青菜各有所爱
4. FastEasyMapping没用过,用过JSONModel、Mantle、MJExtension,其实MJExtension是有自定义属性转换方式的,也可以让开发者自定义类型检测、值过滤来保护App的Crash
5.JSON 属性是 string “2009-04-02T03:35:22Z” Model 属性是 NSDate,为何要自动转成NSDate?应该是由开发者自己去转换成NSDate比较合适、或者重写属性的setter方法,在setter中进行过滤
6.JSON 属性是 string Model 属性是 NSValue,为何要自动转换成nil,这样会直接抛弃服务器返回的值?应该是由开发者自己去转换比较合适,服务器返回的值一般都是有用的、或者重写属性的setter方法,在setter中进行过滤
额。。感谢回复。。
我写的这个东西功能比其他库少很多,所以我也只是提到如有性能需要可以尝试下。。
关于不推荐的那两句话,确实不好,听取你的建议删掉了。
最后关于类型转换的,是之前我在项目中遇到了这样一些情况:本来服务端给的参数是一个 number 类型,经过模型转换后,赋值到一个 NSNumber 的属性中去,但后来服务端不小心改掉了,换成了 string 类型,造成后续访问那个 NSNumber 属性时,实际访问的是 NSString ,然后造成了一些崩溃。开发时有些地方可能漏掉了类型检查,所以我还是希望模型转换时,能更加自动一些,尽量避免各种崩溃问题。
每个工具都有各自的侧重点和设计思路吧,写这个工具时也只是满足了我自己的一些需求,确实有些考虑不周的地方,关于你提的这些问题我再仔细思考一下的 :|
MJ前几个月给我们讲项目时就一直强调写第三方框架时分类方法名要加上前缀,但他自己的MJExtension却没有这么做,哈哈 :lol: 可能因为人家是CEO,太忙了,没有顾及这些小细节。不过没有加前缀,还有这么多人使用,说明功能确实强大,确实厉害呀。我刚才也顺便看了下最新版的MJExtension,全部分类方法名都加上mj_前缀了。还有一点,楼上说得对,MJExtension是有自定义属性转换方式的,可以让开发者自定义类型检测、值过滤来保护App的Crash。我还发现最新版的MJExtension发现类型不匹配时,已经返回nil了,比如服务器返回NSString,属性类型是NSData,会返回nil,如果需要做另行处理,MJExtension一直都有提供值过滤机制。
看到性能评测,确实挺吸引人的,有点小小的疑问?Json内部有复杂的嵌套关系可以转换成功吗?
请教:
一、这几个框架的功能是不对等的,功能强大就意味着逻辑较为复杂,内部的处理流程也多,而YYModel功能极少,速度自然也会快。这文章中的速度比较,就好像是拿“载着10斤货物的自行车”和“载着100斤货物的自行车”比速度一样,肯定是不合理不客观的。如果YYModel功能比Mantle等框架还多,速度还更快,那才是真的快。最后面的tips挺好的,感谢宝贵的分享。
二、没有经过第三方框架作者的同意,就对这些框架指指点点,真的好么?我觉得有点不尊重原作者。而且我们也不了解原作者的设计意图,比如JSONModel为什么不去处理那些类型不匹配的问题,可能作者有自己的想法,他认为这样是合理。所以没有绝对的不合理和不好用。
三、自定义属性转换方式是啥意思?JSONModel和MJExtension都没有这个功能?
四、hash\equal是啥意思?
1. 我对性能评测的部分,功能是对等的,测试代码也能在 Github 上看到,如果有功能方面的差距你可以指出来。我有能力实现上面这些工具的所有功能,且仍然能取得高得多的性能,但这只是 API 设计权衡的问题了。
2. 我仔细研究过这些工具的实现原理,也研究过每个工具的性能瓶颈,也仔细阅读过相关的文档、Github 上的 Issues。JSONModel 处理异常数据时就是有问题,作者也说过它并不会对 JSON 进行校验,Issues 讨论里都有提到:https://github.com/icanzilb/JSONModel/issues?utf8=%E2%9C%93&q=is%3Aissue+crash 我不认为讨论技术问题是对作者的不尊重,每个比较严谨的库都会有人对同类技术进行对比。技术本来就是互相探讨之下才能前进的。
3. Mantle 能自定义两个不同类型对象间转换的方式,FastEasyMapping 的自定义方式就更为详细了,看一下这些工具的文档和头文件就能知道。
4. hash/equal 都不知道我就不得不质疑你的技术水平了。
Sorry,hash\equal在字典转模型中起啥作用?好像不是必须的。
实现对象校对用
评论 ;-) 字典要映射key 没有hash 怎么映射? 对象之间要判等没有equals 怎么判等。。。
一直在用MJExtension,它是有自定义属性转换功能的,还有很多非常便利的功能。
最近也更新了下MJExtension,它的方法名已经有mj_前缀了
评论 :twisted: 很棒,学习了!
评论 :twisted: 确实精彩, 评测写的精彩, 回复的哥们也是非常认真
非常佩服
学习学习~ :idea:
喜欢这样的文章,还有底下的评论也非常到位。希望多点这样氛围的地方学习!
请问博主是用什么工具做的性能测试呢?这些柱状图是工具生成的么?
看了楼主写的代码,很不错,希望能再接再厉!
mark
谢谢分享
我是来支持博主的,赞赞哒,好厉害。 :razz:
为什么要用spinlock? 没太看明白! :arrow:
我记得MJExtension刚出来的时候和YYModel一样,但是后来还是没用IVar了,问了MJ老师,他说是IVar不稳定,可能有问题。
Nope, YYModel 没有用 ivar,用的是 getter 和 setter。
评论 :razz: 大牛,膜拜了!
非常好,中国就需要你这样的程序员。这文章有理有据,内容详实。
博主 是一心偏向cpu啊
觉得YYModel很好用, 谢谢。
不过YYModel现在只支持JSON,但是我现在遇到的项目经常需要使用Xml
只能模仿YYModel使用方式 谢了个基于XMLReader的转换库。
不太会C语言,性能自然差的不行了。
ibireme YYModel有考虑过支持过XML吗?
能借鉴一下基于XMLReader的转换库
Mantle 的错误处理方式更合理.
我只想知道还需要多久才可以看懂里面的代码。。。。(7个月学习经历)
评论 :twisted:
可能是和楼主水平差距太大 想试着看一下你的库 先看下YYModel吧 看不懂怎么破?给推荐点简单的库先看看么? :arrow:
大神…能不能把demo 写的全面一点啊 , 我现在的用法都不知道对不对…
您好,
我是一名iOS初学者,最近在看YYModel的时候遇到一个问题。您在定义@protocol YYModel的时候,说了一句“There’s no need to add ” to your class header.”。难道不需要这样@interface YYBook : NSObject 嘛?。这个我不是很清楚里面具体的道理,所以向您请教一下。谢谢!
自问自答:https://github.com/ibireme/YYKit/issues/168
{
“result”:1001
“desc”:”参数错误”
}
model
@property NSNumber *result;
@property NSString *desc;
@property NSString *other;
正常返回3个属性,错误返回2个属性,这个时候能将只有2个属性的json转换成model吗?
第三个属性值为空,其他两个赋值,我试了下这样的操作貌似是不支持的
这个做了校验,所以出错了,╮(╯▽╰)╭
确实精彩, 评测写的精彩, 回复的哥们也是非常认真
非常佩服 偷个懒 哈哈 :twisted: :twisted: :lol: :lol:
最近准备重构公司APP(大数据分析)的Model,对性能和稳定性方面要求很高。
之前个人也钻研过JSON 转 Model, 性能上还有一定差距,深知不是件容易的事。看了YYModel, 果然厉害。
您好,最近在看您的YYModel的源码部分,请教一下_YYModelMeta和YYClassInfo的关系是什么呢?不都是某个类的信息吗。 :?:
是不是为了其他模块的复用呢
作者你好, 我在友盟的错误捕捉里,收到了一个Application received signal SIGSEGV, 通过符号表定位,发现是crash在NSObject+YYModel 第1279行 ModelDescription. 这有可能是什么原因引起的呢
这个可以在 Github 上提个 Issue,贴一下 Crash Log 或者调用栈来看看。
做最大程度的封装,接口就要设计简单,api简洁,方便实用,这种好的设计我力挺。
最后提一句,如果对性能、网络流量等有更高的要求,就不要再用 JSON 了,建议改用 protobuf/FlatBuffers 这样的方案。JSON 转换再怎么优化,在性能和流量方面还是远差于二进制格式的。
一开始我还以为数据返回的格式大多都是 JSON 或者 XML(这个应该很少了吧),没想到还有 protobuf/FlatBuffers 这样的方案,作者赞 :lol:
好文~ :smile:
是使用Time Profile得到的时间数么
我也是IOS开发者,才写了1年,我最感兴趣的不是性能对比,我想知道怎么作性能对比 :???: 是使用的工具 还是自己写的测试代码
你好,最近看了一下YYModel,准备在项目中使用的时候遇到一个问题,假设有一个类Student,现在是通过JSON可以转换成相对应的Student类,如果已经生成了了Student实例变量stu,是没法通过JSON更新stu的,有考虑支持一下吗?
你好,最近用YYDiskCache做数据的本地持久化,发现在每次写入或更新数据后,sqlite-awl文件都会增大。如果频繁适用setObject:forKey存储的话,sqlite-awl的大小会剧增。
请教一下,我该如何使用,才能避免这个问题呢?
谢谢啦。
厉害了 我的哥 ;-)
“YYModel、MJExtension 都是采用 Category 方式来实现功能,比较灵活,无侵入。”最近在看iOS安全方面的书,很多地方都说了Category 其实就是侵入系统代码,会带来安全隐患。
嗨,能在文章中讲一讲问各个框架产生这种性能差异的原因吗?
来了很多次第一次评论,祝愿早日复出,我是你的脑残粉~
Json生成Model文件(YYModel格式),大神给点建议吧。 https://github.com/zhangs3721/ZZJsonToModel
完全看不懂
学习了 感谢
学习了 感谢
套娃
套娃
套娃
套娃
abababababaabababababaababababababababababababa
作者您好,看了YYModel代码中有YYClassMethodInfo、YYClassIvarInfo这两个类,但实际上并没有用他们。
所以想问下,目前之所以定义他们是为了后续功能做准备吗?还是其他原因?