为什么唱吧iOS 6.0选择了Mantle
2014-12-02 18:06
204 查看
原文:http://www.iwangke.me/2014/10/13/Why-Changba-iOS-choose-Mantle/
最近唱吧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。
由于API使用的开发语言与iOS所使用的Objective-C是截然不同的,所以可能将一些保留关键字作为property的名称(如id),或者不小心override掉基类的属性(如description)。还有可能API中使用了一个很糟糕的名称,或者使用了不符合Objective-C命名规范的名称,这些我们都需要作转换。
只需要实现
好了很多吧?没错,只需要定义一次名称的映射关系就可以了,Mantle负责model与JSON之间的双向转换。不需要将这种逻辑写得到处都是,并且还得维护它的一致性。
iOS中处理URL使用的是NSURL类型,但JSON只支持基本的字符串,Mantle可以自动帮你转换成NSURL。
NSValueTransformer负责在不同类型间进行双向转换,请读者研究一下Mantle的实现方式。在此前提下,留给读者一个问题(其实这是一个真实的故事,类似的故事还有很多,详见iOS应用开发之十大坑队友):
假设我们有一个entity,名字且叫KTVConcreteEntity吧,它有一个属性名字叫entityID,类型是NSInteger。问题来了,entityID可能在另外一个API的response中是字符串类型,在不直接修改Mantle的源码的前提下怎么搞?欢迎在下方留言讨论。
有的时候API的response会有空值,比如
Mantle在这种情况会将newShit转换为nil,但如果是标量如NSInteger怎么办?KVC会直接raise
Mantle是基于KVC给property赋值的,KVC提供了
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,我们来看下代码:
问题完美解决,再也不需要到处写无聊的if/else了。
Mantle为我们带来的方便不胜枚举:
实现了
实现了
提供了
简单且把一件事情做好,不掺杂网络相关的操作
如此强大优雅的设计,让我不得不向Github的工程师们致敬!
篇幅所限,只介绍了几个典型的问题,欢迎大家讨论。但如果你的App的代码规模只有几万行,或者API只有十几个,或者没有遇到我们这些遗留问题,我建议还是不要引入了,杀鸡用指甲刀就够了,杀不动多磨磨找准要害。Anyway,Mantle的实现和思路是值得每位iOS工程师学习和借鉴的。
最近唱吧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命名规范的名称,这些我们都需要作转换。只需要实现
MTLJSONSerializingprotocol并在
+JSONKeyPathsByPropertyKey方法中定义好新旧名称的映射关系即可,Mantle会在序列化及反序列化时对属性名进行自动的转换。
123456789 | + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"identifier": @"id", @"displayDiscription": @"description", @"thisIsANewShit": @"newShit", @"creativeProduct": @"copyToChina", @"betterPropertyName": @"m_wired_propertyName" }} |
Property的类型映射
iOS中处理URL使用的是NSURL类型,但JSON只支持基本的字符串,Mantle可以自动帮你转换成NSURL。123 | + (NSValueTransformer *)URLJSONTransformer { return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];} |
假设我们有一个entity,名字且叫KTVConcreteEntity吧,它有一个属性名字叫entityID,类型是NSInteger。问题来了,entityID可能在另外一个API的response中是字符串类型,在不直接修改Mantle的源码的前提下怎么搞?欢迎在下方留言讨论。
空标量异常
有的时候API的response会有空值,比如copyToChina可能不是每次都有的,JSON是这样儿的:
123 | { "copyToChina": null} |
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,我们来看下代码:
12345678 | @interface MTLModel (KTVNullableScalar)@end@implementation MTLModel (KTVNullableScalar)- (void)setNilValueForKey:(NSString *)key { [self setValue:@0 forKey:key]; // For NSInteger/CGFloat/BOOL}@end |
其它重要特性
Mantle为我们带来的方便不胜枚举:实现了
NSCopyingprotocol,子类可以直接copy是多么爽的事情
实现了
NSCodingprotocol,跟NSUserDefaults说拜拜
提供了
-isEqual:和
-hash的默认实现,model作NSDictionary的key方便了许多
简单且把一件事情做好,不掺杂网络相关的操作
如此强大优雅的设计,让我不得不向Github的工程师们致敬!
写在后面
篇幅所限,只介绍了几个典型的问题,欢迎大家讨论。但如果你的App的代码规模只有几万行,或者API只有十几个,或者没有遇到我们这些遗留问题,我建议还是不要引入了,杀鸡用指甲刀就够了,杀不动多磨磨找准要害。Anyway,Mantle的实现和思路是值得每位iOS工程师学习和借鉴的。
相关文章推荐
- 为什么唱吧iOS 6.0选择了Mantle
- 为什么唱吧iOS 6.0选择了Mantle
- 为什么唱吧iOS 6.0选择了Mantle
- 为什么唱吧iOS 6.0选择了Mantle
- 经验分享:为什么唱吧iOS 6.0选择了Mantle
- 为什么唱iOS 6.0选择Mantle
- 小五:我为什么选择51CTO博客[博友话题] 推荐
- [Erlang 0001] 我们为什么选择Erlang
- 我为什么选择go语言
- [Erlang 0001] 我们为什么选择Erlang
- [原创]FineUI秘密花园(一) &mdash; 为什么选择FineUI?
- Android 相册选择照片或拍照获取图片系统6.0及7.0实现
- 为什么选择数据分析师这个职业?
- 为什么商业搜索引擎选择的索引更新策略是完全重建策略
- 为什么选择Java Web做为学习方向
- 描述应用程序开发者为什么可能选择在UDP上运行应用程序而不是在TCP上运行的原因
- 内地企业为什么要选择香港主机
- 为什么人们普遍选择城市而非农村
- 知乎为什么选择Tornado作为开发框架
- 员工为什么不要高薪而选择离职?