近来的
mt=8" target="_blank" rel="external">iOS的6.0版本号已经成功上线了。
18人月的投入,2500个commit,几十万行的代码改动。
唱吧iOS已经从内至外焕然一新,感谢一起并肩作战的小伙伴们。
6.0一个非常重大的改动就是基于Mantle重建(新建)了Model层。这里不正确Mantle作很多其它介绍。仅仅分享一下使用Mantle的决策及运行过程。
唱吧是一款上线2年多的App,产品形态的演进和迭代很快。
因此不可避免的遗留了各种问题:
除此之外,还有其它project上的约束:
上述的问题都是长期存在且须要解决的。否则严重影响开发效率及代码质量。
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是截然不同的,所以可能将一些保留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之间的双向转换。不须要将这样的逻辑写得到处都是,而且还得维护它的一致性。
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)
问题完美解决,再也不须要到处写无聊的if/else了。
Mantle为我们带来的方便不胜枚举:
NSCopying
protocol,子类能够直接copy是多么爽的事情NSCoding
protocol,跟NSUserDefaults说拜拜-isEqual:
和-hash
的默认实现,model作NSDictionary的key方便了很多如此强大优雅的设计。让我不得不向Github的project师们致敬!
篇幅所限,仅仅介绍了几个典型的问题。欢迎大家讨论。
但假设你的App的代码规模仅仅有几万行,或者API仅仅有十几个,或者没有遇到我们这些遗留问题,我建议还是不要引入了,杀鸡用指甲刀就够了。杀不动多磨磨找准要害。
Anyway。Mantle的实现和思路是值得每位iOSproject师学习和借鉴的。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章