最近唱吧iOS的6.0版本已经成功上线了。18人月的投入,2500个commit,几十万行的代码修改。唱吧iOS已经从内至外焕然一新,感谢一起并肩作战的小伙伴们。

6.0一个很重大的修改就是基于Mantle重建(新建)了Model层。这里不对Mantle作更多介绍,只分享一下使用Mantle的决策及执行过程。

我们遇到的问题

唱吧是一款上线2年多的App,产品形态的演进和迭代非常快。因此不可避免的遗留了各种问题:

  • Model层不健全,没有统一的结构,不同工程师做法差异很大;多数是哑类型,且没有统一的序列化机制
  • 业务逻辑冗余、分散、不一致
  • 模块划分随意,依赖关系混乱,维护困难
  • NSDictionary作为承载业务的数据类型在各处出现(sqlite, Model object, API, Notification, web, OpenURL etc.),参数和值的正确性完全没有编译器检查,字符串很容易写错,风险延后至运行时,易产生低级bug
  • 基本没有文档和注释(结合上一点,不挂debugger很难读懂代码)
  • 几百个API,业务复杂,变动快,重构难;同一个API请求可能有重复和不一致
  • API的一些参数和返回值,同一个参数/返回值可能存在类型差异;由于API需要向前兼容,修改API有成本

除此之外,还有其他工程上的约束:

  • 不能影响现有的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是截然不同的,所以可能将一些保留关键字作为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的工程师们致敬!

写在后面

篇幅所限,只介绍了几个典型的问题,欢迎大家讨论。但如果你的App的代码规模只有几万行,或者API只有十几个,或者没有遇到我们这些遗留问题,我建议还是不要引入了,杀鸡用指甲刀就够了,杀不动多磨磨找准要害。Anyway,Mantle的实现和思路是值得每位iOS工程师学习和借鉴的。

原文链接

为什么唱吧iOS 6.0选择了Mantle相关推荐

  1. iOS 5.0.1完美越狱教程

    这是一份历尽千辛万苦,迟来却依旧让人无比激动的圣诞礼物.就在大约1个小时之前,Pod 2g的博客终于迎来了更新,标题更是醒目的"A4 release",等待终于结束了.Pod 2g ...

  2. iOS 14.0.1修复五大问题,无法连接Wi-Fi已经解决

    最近,苹果发布了iOS 14.0.1正式版,这一版本并没有新功能加入,而是针对此前系统出现的一系列小问题进行了修复,更新包大小在170M左右.此前,iOS 14在更改默认浏览器.邮件APP后,一旦重启 ...

  3. ios更新了系统无服务器,iPhone更新iOS 12.0.1系统失败怎么办?

    iPhone更新iOS 12.0.1系统失败怎么办?有小伙伴反应说系统升级失败.该怎么办呢?下面,小编就为大家介绍下iPhone更新iOS 12.0.1系统失败处理方法. 很多人会选择使用手机直接进行 ...

  4. iOS 12.0+系统录屏(一)

    iOS在录屏功能是走过了长长的一段路,在其他平台尤其是直播平台日益火爆的推动下,iOS12.0终于开放在应用中唤起系统录屏权限给开发者.以下内容以iOS 12.0+为基础,不再讨论之前的版本. 理论准 ...

  5. Swift 与 Objective-C:您应该为下一个 iOS 移动应用选择哪个?

    2014 年,Apple 推出了一种更简单的新方法来构建 iOS 应用程序 - Swift.然而,在 2020 年,IT 经理和 CTO 仍然难以在 Objective-C 和 Swift 之间做出选 ...

  6. iPhone / iPad iOS 5.0.1完美 越狱 教程 A4/A5处理器

    黑客团队Dev Team的红雪列越狱软件Redsn0w 0.9.10b1,可以完美越狱部分iOS 5.0.1设备,1.此次完美越狱工具支持机型: iPhone 3GS iPhone 4 iPhone ...

  7. iOS-Xcode8新特性(Code signing is required for product type 'Application' in SDK 'iOS 10.0')

    今天我们APP发布现版本,我什么东西都做好了,然后我选择配置文件和证书,发现有个问题一直报错,我升级xcode之前是好的: 错误描述如下: poseidon has conflicting provi ...

  8. iOS 13.0 暗黑模式的适配

    一.暗黑模式的原理 将原本的资源文件,创建出两种不同的模式.根据不同的模式,自动获取该样式的资源. 每次切换系统模式的时候,系统会重新调用一些方法,重新赋值. 二.暗黑模式适配主要考虑的是: 1. 图 ...

  9. Chronic/iPhone Dev Team联合发布iOS 5.0.1完美越狱

    这是一份历尽千辛万苦,迟来却依旧让人无比激动的圣诞礼物.就在大约1个小时之前,Pod 2g的博客终于迎来了更新,标题更是醒目的"A4 release",等待终于结束了. Pod 2 ...

最新文章

  1. 剑指offer--day07
  2. 高并发下如何保证接口的幂等性?
  3. MSP430学习笔记
  4. Java数据库连接(JDBC)之一:JDBC详细连接过程
  5. 变量命名规则_Java变量与常量
  6. 芒果数据库备份与还原
  7. 湖南省长沙市谷歌高清卫星地图下载
  8. 使用词和图嵌入来衡量统一医学语言系统概念之间的语义相关性
  9. Linux 与 Python编程2021 Python面向对象编程实训 educoder实训
  10. gromacs ngmx_GROMACS教程
  11. FER 人脸情绪识别系统
  12. 2022-2028全球与中国多通道光纤旋转接头(FORJ)市场现状及未来发展趋势
  13. Elasticsearch实战(十三)---聚合搜索Aggs聚合及Count,Avg操作
  14. 【数字IC手撕代码】Verilog偶数分频|题目|原理|设计|仿真(二分频,四分频,六分频,八分频,偶数分频及特殊占空比)
  15. 立体栅格地图_网格Mesh-3D立体图形-教程-地图 JS API | 高德地图API
  16. 计算机没有autoCAD_BKM ? 37期 — 教你Python玩转AutoCAD
  17. 基于多目标算法的冷热电联供型综合能源系统运行优化 多目标粒子群 冷热电联供 综合能源系统 运行优化
  18. python计算圆周率
  19. 营销策划方案示范文本
  20. 人见人“吐”的十大经典广告(绝对超经典)[zzzz]

热门文章

  1. 弘辽科技:人少开店做什么生意好?暴利生意项目
  2. 04 BTSnoop
  3. 简单绕过EAC反作弊检测分析【1】
  4. 小程序的目录结构—pages文件夹的内容和作用
  5. 在Ubuntu系统下编写简单程序
  6. 幻读是什么,幻读有什么问题?
  7. 齿轮过载、偏心、不对中、断齿特征分析及信号特征提取
  8. julia 使用记录
  9. 【爬虫】爬取煎蛋上的段子
  10. ThinkPHP 3.获取get post参数与I方法