在当前项目的一些内容加工逻辑较多的界面,我会使用ViewModel来对Model进行一层包装,这样可以保持Model的纯净,也可以减少Controller中弱逻辑代码的堆叠。当然,部分公用内容也是可以通过给Model添加分类来实现的,ViewModel更多是提供特定页面的定制化内容。
由于项目并没有采用MVVM模式,也就没有引入ReactiveCocoa,所以项目里面比较多这样的代码:
1 | NSArray <TBVEmployee *> *employees = @[[TBVEmployee new]]; |
这段代码主要是为了将Model 转化成ViewModel。
这里可以给NSArray增加一个tbv_map方法,让这段代码阅读起来更加函数式:
1 | @interface NSArray (SwiftOperation) |
添加分类后,上面那段代码可以这样写:
1 | NSArray *employees = @[[TBVEmployee new]]; |
嗯!看起来清爽了不少。但是写多了之后会有一个小瑕疵:为了能map到NSArray可容纳的所有类型,block的传参使用了id类型,当需要使用形参的个别属性时,我需要手动更改id为具体的类名:
1 | NSMutableArray *items = [employees tbv_map:^id(TBVEmployee *value) { |
有没有什么法子,能让Xcode的智能提示帮我直接预测到想要map的元素类型呢?
答案是Objective-C的范型。虽然Objective-C对于范型的支持还是比较弱的,但是处理当前的这个需求还是可以的。
先给NSArray分类添加范型:
1 | @interface NSArray <T> (SwiftOperation) |
然后在使用时,指定需要map数组的元素类型:
1 | NSArray <TBVEmployee *> *employees = @[[TBVEmployee new]]; |
然后Xcode就会根据数组元素的类型,做出智能提示啦:

以此还可以增加其它的操作:
1 | @interface NSArray <T> (SwiftOperation) |
这里只添加了NSArray类型的操作,NSDictionary、NSSet这类集合类型也可以以此类推来实现。不过上面的方法只是做了一层简易的包装,并没有延迟计算啥的,只是让我写起来能更加开心、舒畅点吧。
更改
2017-07-06 :
需要注意的是 instancetype 会包含范型检查,为了避免使用者过多地进行强制类型转换, tbv_map 可以修改成如下形式:
1 | - (NSArray *)tbv_map:(id (^)(T value))block; |