前一篇博客最后介绍了KVC 再json 转模型时遇到一些问题。今天接着来介绍KVC 的其他用法。其实我们在一开始的时候就一直再强调命名的重要性。命名规范是KVC 存活的基础。如果没有这个条件支撑,那么KVC使用起来就不会那么简单了。在这里大王再哔哔几句,作为一个程序员,不管我们长得有多丑,我们的代码一定好看。一段规范的代码代表我们的脸面,也是程序员成熟的标志。既然说到了命名,那么就再来看看KVC 让人吃惊的一面。
1)KVC 方法的搜索顺序
当你看到这个标题的时候,可能会诧异,说的是啥啊?啥是搜索顺序啊?别着急,在介绍调用顺序之前,我们先来看一段很简单的代码,先热身一下。假设我们有一个Person类,这个类是个宅男,头文件中啥玩意都没有。既然头文件中没有任何属性,我们就直接看实现文件。
#import "Person.h" @interface Person () @property(nonatomic,copy)NSString* yourHand; @end @implementation Person -(instancetype)init { self=[super init]; if (self) { self.yourHand=@"你的右手"; } return self; } -(NSString*)girlFriend { NSLog(@"%s",__func__); return self.yourHand; } @end
为了防止宅男太过孤单,作为上帝的我,给他创建了手,并在一出生(init)时就给它赋值了。并且创建了一个girlFriend方法。如果我们想调用这个方法直接
[person girlFriend]是不行的(但是我们可以[person performSleector:@selector(girlFriend)],这是因为runtime的关系,这里不详细介绍,可以参看我前几篇博客),在这里,我们还可以这样调用
- (void)viewDidLoad { [super viewDidLoad]; Person *person=[[Person alloc] init]; NSLog(@"%@",[person valueForKey:@"girlFriend"]); }
2015-04-27 20:56:37.207 KVC[19591:953199] -[Person girlFriend]
2015-04-27 20:56:37.207 KVC[19591:953199] 你的右手
了解了上面的内容,我们就可以来说说KVC与容器类。什么是容器类呢?说简单点就是数组和集合。这里没啥可说的,还是先看代码吧。还是这个Person类,我们先来稍微改动一下。头文件还是什么都没有,实现文件变为如下
#import "Person.h" @implementation Person -(NSUInteger)countOfFingers { return 10; } -(id)objectInFingersAtIndex:(NSUInteger)index { NSLog(@"这是第%lu个手指",index); return @(index); } @end
光看这段代码不神奇,再看一下测试方法和打印信息,我们就会感到很吃惊。
- (void)viewDidLoad { [super viewDidLoad]; Person *person=[[Person alloc] init]; NSLog(@"%@",[person valueForKey:@"fingers"]); }
2015-04-27 21:10:53.990 KVC[19658:958083] 这是第0个手指
2015-04-27 21:10:53.991 KVC[19658:958083] 这是第1个手指
2015-04-27 21:10:53.991 KVC[19658:958083] 这是第2个手指
2015-04-27 21:10:53.991 KVC[19658:958083] 这是第3个手指
2015-04-27 21:10:53.991 KVC[19658:958083] 这是第4个手指
2015-04-27 21:10:53.991 KVC[19658:958083] 这是第5个手指
2015-04-27 21:10:53.991 KVC[19658:958083] 这是第6个手指
2015-04-27 21:10:53.992 KVC[19658:958083] 这是第7个手指
2015-04-27 21:10:53.992 KVC[19658:958083] 这是第8个手指
2015-04-27 21:10:53.992 KVC[19658:958083] 这是第9个手指
2015-04-27 21:10:53.992 KVC[19658:958083] (
0,
1,
2,
3,
4,
5,
6,
7,
8,
9
)
全过程我们都没有直接调用countOfFingers 和-(id)objectInFingersAtIndex:(NSUInteger)index 方法。由打印信息可以知道是系统帮我们调用了。并且有打印信息我们可以知道,系统把fingers 当成属性,并且这个“属性”是数组类型的。当我们调用valueForKey:时,系统会按如下顺序调用方法
如果都找不到的话,系统会调用valueForUndefinedKey:方法。以上所讲的都是针对不可变的容器类。如果是可变的容器类,我们可以可以这样使用
dataM= [data mutbaleArrayValueForKey:@"属性"]。当dataM改变的时候,会向data发送消息,data中的内容也会跟着改变。这里我就不详细演示了,有兴趣的可以自己试一下。
2)高阶消息传递
大家可能冷不丁的看到高阶这个词感觉很高大上,仿佛又回到了大学的高数课堂。那么什么是高阶消息传递呢?说白了就是让数组中的每一个元素都执行某个方法,并把结果返回到新的数组中。这下感觉不那么高大上了吧?下面来看一个经典的例子代码
- (void)viewDidLoad { [super viewDidLoad]; NSArray *array=@[@"烤串",@"啤酒",@"爆米花"]; NSArray *lengths=[array valueForKeyPath:@"capitalizedString.length"]; NSLog(@"%@",lengths); }
2015-04-27 21:53:25.531 KVC[19821:972219] (
2,
2,
3
)
我们可以看出,数组中的每一项都执行了capitalizedString 方法,执行完后,又执行了length 方法。然后把结果返回到新的数组中。在开发中,我们可以适当的使用这个方法,会减少我们的代码量。
尽管KVC很高大上,但是我们现实开发中,还是谨慎地使用,因为它太过于强大,贸然使用可能会无意间破坏程序的封装性。所以我们只有想清楚了之后再使用,而且最好在使用的地方加上注释。KVC 是大招,我们要在必要的时候使用,动画片里孙悟空也不是在一开始就放大招,你说是不?