公司项目之前的model层代码是我使用JSON工具直接生成Objective-C代码的,当时还是觉得相当省事的,毕竟我经历过无model层的NSDictionary“黑暗”时期。但是随着项目的推进,问题开始慢慢显现出来。

 
于是,在一个多月前,我在Objc.io上看到提及了Mantle,花了一点时间看了一下,决定先在分支上全套改用Mantle。使用了一段时间,性能没造成什么瓶颈,稳定性还是可以接受的。后来也基于Mantle、MK和RAC,把网络的请求整合在一起,在新项目上全面使用。一开始时也是没什么问题的,但后来我也逐渐发现了Mantle虽好,但不至于能解决一切问题。最近看到了《为什么唱吧iOS 6.0选择了Mantle》文章,我决定写博客记下一些model层的坑。
 
 
 
简要分析
 
先来简要分析一下各种构建model层方法的优点和缺点:
 
一、工具生成model
 
优点:
 
1、简单易用,新手也可以10秒上手
 
2、有一定的容错代码
 
3、代码生成相对工整和规范,部分工具还可以选择是否使用ARC
 
4、生成简单model耗时少
 
缺点:
 
1、工具生成的类名或者属性名不太符合要求,往往需要自行修改,但是修改起来相当麻烦,却需要相当专注以防有什么地方忘记修改。
 
2、生成的代码相当冗长。
 
3、对适应字段变化比较麻烦,一旦属性需要修改字段时,要么人工修改,要么重新生成,但是极有可能需要重复缺点1的步骤。
 
4、model之间一些继承关系还是需要自行修改继承来实现。
 
 
 
二、基于运行时生成的model(Mantle这类)
 
优点:
 
1、减少大量模版代码
 
2、修改字段映射时相当简便
 
3、扩展时相对方便
 
4、可以实现更多复杂的映射关系和数值转换
 
5、调试时的异常可以较好地发现问题
 
6、实现了NSCopying和NSCoding协议,可以轻松序列化
 
缺点:
 
1、基于运行时属性映射,对性能有一定影响
 
2、有部分容错处理需要自行解决,否则很可能崩溃(下文详解)
 
3、框架代码不少
 
 
 
三、NSDictionary型model
 
优点:
 
1、无需任何基础,直接可用
 
2、容错性相对较高
 
3、无视任何数据结构,均能适应
 
缺点:
 
1、维护成本昂贵
 
2、编译器无法检查拼写,需要定义大量key的常量,否则极其容易写错
 
3、调试相对麻烦
 
 
 
其实,NSDictionary型model还是有一定用途的,毕竟有些情况下,不需要浪费精力去构建一个很短小或者很快就会被释放的model。但大多数情况下,还是需要去构建一个合理的model,来保证项目的健壮性和开发效率。以前,我老大和我说,iOS应用MVC三层,M这一层其实服务端已经帮你完成了大部分,来到客户端再自己处理model,既消耗性能又降低了开发效率。当时,我觉得还是比较正确的,但随着MVC的C变得臃肿不堪,M变得越来越轻量的时候。很多东西都耦合在controller,model这层能做好的话,就能一定程度上减轻了controller的复杂度。加上工作了以后发现,一个只有NSDictionary,无真正model的商业应用,真的非常不利于维护。
 
基于上述的种种理由,我还是决定了正式全面使用Mantle。但Mantle不是万能的,我还是遇到了几个问题。
 
 
 
null值
 
如果你的属性是基本数值类型的话,JSON返回一个null值,那么在Mantle生成model的时候,果断崩溃了。这个问题和解决方案跟《为什么唱吧iOS 6.0选择了Mantle》中的一样,model中实现一下setNilValueForKey:方法即可。建议使用基类继承,那么写一次这个方法就所有model都解决了这个问题。
 
 
 
键值的合理映射
 
复制代码
1 {
2     "code": 1
3     "result":{
4         "access_token":"m_xxxxxxx",
5         "user_id":1111
6     }
7 }
复制代码
例如上述JSON,假设整个JSON是一个model,那么如果直接按照JSON的格式来映射,就要新建一个“result”额外的model类。但或许不需要这么繁复,其实可以这么写
 
1 + (NSDictionary *)JSONKeyPathsByPropertyKey
2 {
3     return @{@"accessToken": @"result.access_token",
4                   @"userId": @"result.user_id"};
5 }
这样做就可以很方便地映射到对应的属性上,同时也不需要额外新建一个model类。这里为什么没写“code”的映射呢,因为如果属性名和JSON的键名一致时,是可以省略不写映射的,具体大家可以看看Mantle的源码
 
 
 
值的类型问题
 
这个问题是最棘手的,不能说后台坑队友,但是JSON的数值类型和文档不符乃家常便饭。作为和用户最近的前线,我只能想尽办法去收尾,不能完全放任不管吧。常规的方法不外乎以下几种:
 
1、转换model前先进行预处理JSON数据,把类型不符的值转换或者删除掉
 
2、为这些容易崩溃的值,都写上NSValueTransformer的转换
 
 
 
这些的确都能解决问题,但是效率就下降了很多,你得关注各种各样的可能出现的情况。相信我,要是你这么做,你连睡觉都睡不好。
 
我列举一下几种类型不符会导致的异常:
 
1、属性是BOOL类型,返回值是string类型。
 
2、属性是NSString类型,返回值是number类型,Mantle只会转换出NSNumber类型。你调用length等NSString的专用方法时,你懂的。
 
3、使用了类似上面"result.access_token"的映射,但返回值不是object类型(例如array类型)。
 
4、属性是NSArray,使用了转换,返回值是object。
 
 
 
放心,实际情况中,绝对不会只有上述4种可能的。不过幸运的是,Mantle帮大家处理了1、3、4这些情况(如果object和array都是使用了转换方法的话,在转换的时候会处理这些异常的),只会在调试模式下抛出异常,Release的时候是不会崩溃的。如果大家不想在调试的时候被这些异常打断的话,可以注释掉MTLValidateAndSetValue这个方法中的对应代码。
 
接下来大家可能觉得太匪夷所思了,为什么一个小小的客户端还得因为用个Mantle就要去规避这么多陷阱。我就是遇到这么多陷阱,想到了解决方法,自己也是成长了。针对JSON的类型,我有了以下的考虑:
 
JSON其实就是只有4种类型,string、number(int 、bool…)、object、array。在Objective-C对应也就是,NSString、NSNumber、NSDictionary、NSArray,因此要规避类型问题也是从这几个类着手。这里要说一下为什么属性有int、bool等,我只归了一类NSNumber。因为实质上,Mantle只是转换了NSNumber类型的对象出来,在setValue的时候,是由系统根据类型调用了对应的NSNumber方法。由于number和string的类型错误是最常见,同时也是最隐蔽的(调用类似intValue的方法看不出端倪),为此我写了一个AvoidMTLModelCrash的category,github上的地址,使用了这个category以后,关于NSString和NSNumber类型问题的崩溃基本都可以解决。如果是NSArray或者NSDictionary设置到NSString的属性也是无法检测的,但是面对如此严重的类型问题,我建议还是和小伙伴一起坐下来,好好谈谈“人生”吧。
 
 
 
灵活的转换
 
例如某些接口返回的数据是数组,但很多时候只需要用到这个数组的第一个元素。我们可以直接将数组里面的一个元素影射出来。
 
1 + (NSValueTransformer *)pointListJSONTransformer
2 {
3     return [MTLValueTransformer transformerWithBlock:^id(NSArray *array) {
4         return [array firstObject];
5     }];
6 }
除了属性映射和每个属性固定的类型转换,MTLJSONSerializing的协议还有classForParsingJSONDictionary这么一个方法可以改变解释后的类。例如B、C均继承A,用Mantle生成A类对象。A类可以通过这个方法,选择不同的子类生成对象。但是对外可见的接口也是A类的接口,这样就类似NSArray一样(NSArray其实也是有很多子类的)

iOS的Mantle实战分析的更多相关文章

  1. 《iOS应用逆向工程:分析与实战》

    <iOS应用逆向工程:分析与实战> 基本信息 作者: 沙梓社    吴航    刘瑾 丛书名: 信息安全技术丛书 出版社:机械工业出版社 ISBN:9787111450726 上架时间:2 ...

  2. 新书《iOS应用逆向工程:分析与实战》

    前无古人!小白福音!国内第一本iOS应用逆向工程类图书<iOS应用逆向工程:分析与实战>就要空降啦~! 你是否曾因应用上线的第一天即遭破解而无奈苦恼,想要加以防范,却又束手无策? 你是否曾 ...

  3. 聚合数据 iOS 项目开发实战:条码查询器

    记录下,聚合数据 iOS 项目开发实战:条码查询器:视频地址:http://www.jikexueyuan.com/course/324.html 条码查询API:https://www.juhe.c ...

  4. elk实战分析nginx日志文档

    elk实战分析nginx日志文档 架构: kibana <--- es-cluster <--- logstash <--- filebeat 环境准备:192.168.3.1 no ...

  5. 【HELLO WAKA】WAKA iOS客户端 之一 APP分析篇

    由于后续篇幅比较大,所以调整了内容结构. 全系列 [HELLO WAKA]WAKA iOS客户端 之一 APP分析篇 [HELLO WAKA]WAKA iOS客户端 之二 架构设计与实现篇 [HELL ...

  6. 包建强的培训课程(7):iOS企业级开发实战

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  7. 实战分析: MySQL字符集

    原创: 吴炳锡 MySQLBeginner 实战分析: MySQL字符集说明 在本文中讨论以下几个问题: 1. GBK和UTF8占用几个字节 2. ASCII码在不同字符集中占用几个字节 3. MyS ...

  8. iOS项目开发实战——学会使用TableView列表控件(四)plist读取与Section显示

    文本将会实现把数据存储到plist文件里.然后在程序中进行读取.在TableView控件中依据不同的类别显示Section. 有关TableView 的其它实现,请參考<iOS项目开发实战--学 ...

  9. Java互联网架构-Mysql分库分表订单生成系统实战分析

    概述 分库分表的必要性 首先我们来了解一下为什么要做分库分表.在我们的业务(web应用)中,关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量.连接数.处理能力等都很有限,数据库本身的“有状态性” ...

随机推荐

  1. 分享form表单提交问题

    前段时间做了一个form表单传值问题  当时觉得form表单的submit不就是提交form表单name的value值吗 ? 其实是对的  但是我做的是一个打印页面  需要把当前页面的元素传入下一个u ...

  2. 【CSS】Beginner2:Selectors, Properties, and Values

    1.Whereas HTML has tags,CSS has selectors.   2.Selector{ properties:value; properties2:value2; }   3 ...

  3. vijosP1016 北京2008的挂钟

    vijosP1016 北京2008的挂钟 题目链接:https://vijos.org/p/1016 [思路] Dfs. 对操作搜索更加优秀,所以采用搜索每一个操作的使用次数,因为操作数为4则相当于没 ...

  4. Yii 图片FTP批量上传 并生成缩略图

    图片批量上传,前台使用 uploadify.swf,这个就不介绍了.这里使用两个扩展,一个是FTP上传的扩展,还有一个是生成缩略图的扩展地址:http://www.yiiframework.com/e ...

  5. lab 1实验报告

    练习1:理解通过make生成执行文件的过程. 1.操作系统镜像文件ucore.img是如何一步一步生成的? 生成 bin/kern 部分 生成 init.o 生成 readline.o 生成 stdi ...

  6. 以管理员身份启动ClickOnce部署的应用程序

    ClickOnce方式部署应用简单方便,估计很多人都用过,但这种方式存在一定的“缺陷”,即以管理员方式启动应用的问题,虽然出于安全考虑可以理解,但给需要管理员权限才能正常运行的程序带来了一定的麻烦,这 ...

  7. 使用freemarker生成html

    http://herryhaixiao.iteye.com/blog/677524 由于freemarker这个技术很久很久就有了,注释我就没写得很详细了,相信大家都看得懂.下面就直接上代码以及一些代 ...

  8. oracl使用DataBase Configuration Assistant创建、删除数据库

    原文:oracl使用DataBase Configuration Assistant创建.删除数据库 可以使用DataBase Configuration Assistant来创建一个心得数据库.Da ...

  9. 解决libcrypto.so.0.9.8: cannot open shared object file

    文章解决的问题:安装nginx中需要libmysql.so.16包的支持,下面介绍如何安装,并建立lib的连接. 问题展示:error while loading shared libraries: ...

  10. gitservergitlab之搭建和使用

    gitserver比較有名的是gitosis和gitolite,这两个管理和使用起来略微有些复杂,没有web页面,而gitlab则是类似于github的一个工具,github无法免费建立私有仓库,而且 ...