为什么唱iOS 6.0选择Mantle
近来的 mt=8" target="_blank" rel="external">
18人月的投入,2500个commit,几十万行的代码改动。
唱吧iOS已经从内至外焕然一新,感谢一起并肩作战的小伙伴们。
6.0一个非常重大的改动就是基于Mantle重建(新建)了Model层。这里不正确Mantle作很多其它介绍。仅仅分享一下使用Mantle的决策及运行过程。
我们遇到的问题
唱吧是一款上线2年多的App,产品形态的演进和迭代很快。
因此不可避免的遗留了各种问题:
- Model层不健全。没有统一的结构。不同project师做法差异非常大;多数是哑类型。且没有统一的序列化机制
- 业务逻辑冗余、分散、不一致
- 模块划分任意,依赖关系混乱,维护困难
- NSDictionary作为承载业务的数据类型在各处出现(sqlite, Model object, API, Notification, web, OpenURL etc.)。參数和值的正确性全然没有编译器检查,字符串非常easy写错,风险延后至执行时,易产生低级bug
- 基本没有文档和凝视(结合上一点,不挂debugger非常难读懂代码)
- 几百个API,业务复杂。变动快,重构难;同一个API请求可能有反复和不一致
- API的一些參数和返回值,同一个參数/返回值可能存在类型差异;因为API须要向前兼容,改动API有成本
除此之外,还有其它project上的约束:
- 不能影响现有的API。全部的事情仅仅限于iOS端的改动
- 代码即文档,由于没有精力维护文档
- 对不同Model的持久化方式作迁移
- 避免写大段枯燥的Model的序列化/反序列化代码
- 没有时间造出足够成熟、健壮可重用的组件及撰写文档
上述的问题都是长期存在且须要解决的。否则严重影响开发效率及代码质量。
11年的时候我还在做社交游戏的时候,设计并实现了一套简单的基于Objective-C Runtime的数值表Model结构及转换工具(Model<=>csv)供数值策划使用。但想写出一套成熟的方案还是有一些距离,并且也没有资源和时间作维护、測试和文档。
顺着这个思路找到了JSONModel和Mantle,前者刚刚1.0。后者在Github for Mac中广泛使用且社区更成熟(甚至Slack上有channel),所以成为了更好的选择。
事实也证明这个选择是对的,6.0上线后。crash率比之前的版本号有显示的减少。而且Mantle相关的crash占总crash的比率不到3%,大能够直接用在大型的产品上。
除了成熟稳定。Mantle基本攻克了我们遇到了的全部问题。
以下详细介绍一些通用性Mantle使用经验。主要的用法请直接移步Mantle的README。
Property名称转换
因为API使用的开发语言与iOS所使用的Objective-C是截然不同的,所以可能将一些保留keyword作为property的名称(如id),或者不小心override掉基类的属性(如description)。还有可能API中使用了一个非常糟糕的名称,或者使用了不符合Objective-C命名规范的名称,这些我们都须要作转换。
仅仅须要实现MTLJSONSerializing
protocol并在+JSONKeyPathsByPropertyKey
方法中定义好新旧名称的映射关系就可以。Mantle会在序列化及反序列化时对属性名进行自己主动的转换。
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{
@"identifier": @"id",
@"displayDiscription": @"description",
@"thisIsANewShit": @"newShit",
@"creativeProduct": @"copyToChina",
@"betterPropertyName": @"m_wired_propertyName"
}
}
好了非常多吧?没错,仅仅须要定义一次名称的映射关系就能够了,Mantle负责model与JSON之间的双向转换。不须要将这样的逻辑写得到处都是,而且还得维护它的一致性。
Property的类型映射
iOS中处理URL使用的是NSURL类型,但JSON仅仅支持主要的字符串。Mantle能够自己主动帮你转换成NSURL。
+ (NSValueTransformer *)URLJSONTransformer {
return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}
NSValueTransformer负责在不同类型间进行双向转换。请读者研究一下Mantle的实现方式。在此前提下,留给读者一个问题(事实上这是一个真实的故事。类似的故事还有非常多,详见iOS应用开发之十大坑队友):
如果我们有一个entity,名字且叫KTVConcreteEntity吧。它有一个属性名字叫entityID,类型是NSInteger。问题来了。entityID可能在另外一个API的response中是字符串类型,在不直接改动Mantle的源代码的前提下怎么搞?欢迎在下方留言讨论。
空标量异常
有的时候API的response会有空值,比方copyToChina
可能不是每次都有的,JSON是这样儿的:
{
"copyToChina": null
}
Mantle在这样的情况会将newShit转换为nil,但假设是标量如NSInteger怎么办?KVC会直接raise NSInvalidArgumentException
。
Mantle是基于KVC给property赋值的,KVC提供了- (void)setNilValueForKey:(NSString *)key
方法,让我们为nil指定一个合理的替代值。我们来看一下此方法的解释:
Invoked by setValue:forKey: when it’s given a nil value for a scalar value (such as an int or float).
Subclasses can override this method to handle the request in some other way, such as by substituting 0 or a sentinel value for nil and invoking setValue:forKey: again or setting the variable directly. The default implementation raises an NSInvalidArgumentException.
对于标量来讲。多数情况下合理的值即为0,我们来看下代码:
@interface MTLModel (KTVNullableScalar)
@end
@implementation MTLModel (KTVNullableScalar)
- (void)setNilValueForKey:(NSString *)key {
[self setValue:@0 forKey:key]; // For NSInteger/CGFloat/BOOL
}
@end
问题完美解决,再也不须要到处写无聊的if/else了。
其他重要特性
Mantle为我们带来的方便不胜枚举:
- 实现了
NSCopying
protocol,子类能够直接copy是多么爽的事情 - 实现了
NSCoding
protocol,跟NSUserDefaults说拜拜 - 提供了
-isEqual:
和-hash
的默认实现,model作NSDictionary的key方便了很多 - 简单且把一件事情做好。不掺杂网络相关的操作
如此强大优雅的设计。让我不得不向Github的project师们致敬!
写在后面
篇幅所限,仅仅介绍了几个典型的问题。欢迎大家讨论。
但假设你的App的代码规模仅仅有几万行,或者API仅仅有十几个,或者没有遇到我们这些遗留问题,我建议还是不要引入了,杀鸡用指甲刀就够了。杀不动多磨磨找准要害。
Anyway。Mantle的实现和思路是值得每位iOSproject师学习和借鉴的。
为什么唱iOS 6.0选择Mantle的更多相关文章
- embedded dylibs/frameworks are only supported on iOS 8.0 and later 错误解决
ld: warning: embedded dylibs/frameworks only run on iOS 8 or later ld: embedded dylibs/frameworks ar ...
- 在 iOS 10.0 之后, App 要调用手机相机与相簿应注意的事项
iOS 的 SDK 每一年至少都会有一次大改版,从 2009 到 2016 年,版号已经到了第 10 版了,很轻易的就追上了 Mac OSX. 每一次的大改版都会有不少新的功能或新的规范,在 iOS ...
- iOS 从0到1搭建高可用App框架
iOS 从0到1搭建高可用App框架 最近在搭建新项目的iOS框架,一直在思考如何才能搭建出高可用App框架,能否避免后期因为代码质量问题的重构.以前接手过许多“烂代码”,架构松散,底层混乱,缺少规范 ...
- Xcode 9.0 报错, Safe Area Layout Guide Before IOS 9.0
Xcode 9.0 新建工程报错 xcode Safe Area Layout Guide Before IOS 9.0 如下图,在Builds for 选择iOS9.0 and Later,不勾选U ...
- iOS 9.0中UIAlertController的用法。
1.我为什么要写这篇博客记录它? 答:因为 UIAlertView和UIActionSheet 被划线了 苹果不推荐我们使用这两个类了,也不再进行维护和更新,为了以后方便使用我来记录一下.如图所示 正 ...
- iOS 15 无法弹出授权弹框之解决方案---Your app uses the AppTrackingTransparency framework, but we are unable to locate the App Tracking Transparency permission request when reviewed on iOS 15.0
2021年9月30日下午:我正愉快的期盼着即将到来的国庆假期,时不时刷新下appstoreconnect的网址,28号就提上去的包,今天还在审核中....由于这个版本刚升级的xcode系统和新出的iO ...
- HierarchyViewer for iOS 2.0 BETA Introduction
We know HierarchyViewer is an useful tool in Android SDK. The developer and tester, who haven't the ...
- iOS 10.0 更新点(开发者视角)
html, body {overflow-x: initial !important;}html { font-size: 14px; } body { margin: 0px; padding: 0 ...
- iOS 7.0获取iphone UDID 【转】
iOS 7.0 iOS 7中苹果再一次无情的封杀mac地址,使用之前的方法获取到的mac地址全部都变成了02:00:00:00:00:00.有问题总的解决啊,于是四处查资料,终于有了思路是否可以使用K ...
随机推荐
- win8.1 “服务器运行失败”的解决方法
平台:win8.1 SP1 问题:安装QQ安全管家又卸载后出现了奇怪的问题,1.在桌面点右键→个性化时,提示“服务器运行失败”.2.右键点击“这台电脑”,选择“属性”时没有反应.3.开始屏幕里随便选择 ...
- 关于idea新建子目录时往父目录名字后叠加而不是树形结构的解决方法(转)
我们在IDEA中创建子目录时,子目录总是在父目录后面叠加而不是树形,如下 我们可以打开项目窗口的右上角的设置标志, 将红圈选项的√先去掉,创建好子目录后再将它选中就可以
- [WPF自定义控件库]排序、筛选以及高亮
1. 如何让列表的内容更容易查找 假设有这么一个列表(数据源在本地),由于内容太多,要查找到其中某个想要的数据会比较困难.要优化这个列表,无非就是排序.筛选和高亮. 改造过的结果如上. 2. 排序 在 ...
- 用FATFS在SD卡里写一串数字
用FATFS写SD卡,如写入数组 s[] ={1,2,3,4,5,6} 想要在txt中显示“123456” 就要把 s[0]=1+'0' 或 s[0]=1+48 或 s[0]=1+0x30 ...
- AIR 初步 Javascript学习之cookie操作
//设置cookie的名称,值,过期时间 function setCookie(cookieName,cookieValue,cookieExpire) { v ...
- arcengine,深入理解游标Cursors,实现数据的快速查找,插入,删除,更新
风过无痕 原文 arcengine,深入理解游标Cursors,实现数据的快速查找,插入,删除,更新 深入理解游标Cursors,实现数据的快速查找,插入,删除,更新 1.查找数据Search Cu ...
- IfSpeed 带宽计算
http://www.360doc.com/content/11/0304/22/2614615_98214710.shtml http://www.cisco.com/support/zh/477/ ...
- JavaScript、Ajax与jQuery的关系 分类: C1_HTML/JS/JQUERY 2014-07-31 10:15 3388人阅读 评论(0) 收藏
简单总结: 1.JS是一门前端语言. 2.Ajax是一门技术,它提供了异步更新的机制,使用客户端与服务器间交换数据而非整个页面文档,实现页面的局部更新. 3.jQuery是一个框架,它对JS进行了封装 ...
- 【Lucene4.8教程之三】搜索 2014-06-21 09:53 1532人阅读 评论(0) 收藏
1.关键类 Lucene的搜索过程中涉及的主要类有以下几个: (1)IndexSearcher:执行search()方法的类 (2)IndexReader:对索引文件进行读操作,并为IndexSear ...
- Android 为开发者准备的最佳 Android 函数库(2016 年版)
本文是翻译自 CloudRAIL 的官方博客(https://cloudrail.com/best-android-libraries-for-developers/),本文中分享的 Android ...