原创Blog,转载请注明出处
blog.csdn.net/hello_hwc
前言:本文的架构
KVC的定义
KVC的几个场景
希望,通过本文让不了解KVC的同学入门,KVC在IOS开发中是个比较重要的概念,也是理解KVO的基础。
一 KVC的定义
KVC的全称是key-value coding,通过key-value的方式来访问属性。在很多地方,KVC是非常方便的。
属性
@property (strong,nonatomic) NSString * message;
赋值
[self setValue:@"blog.csdn.net/hello_hwc" forKey:@"message"];
取值
NSString * message = [self valueForKey:@"message"];
除了对象可以用KVC,对标量也可以用KVC,因为Foundation对标量的KVC进行了内部转换。
例如
@property (nonatomic) int number;
[self setValue:@(10) forKey:@"number"];
NSNumber * num = [self valueForKey:@"number"];
以上讲的都是valueForKey,KVC也支持valueForKeyPath,这里的path是字符串路径,用点来隔开,逐层深入。
例如
所以,valueForKeyPath对于复杂的集合类处理以及对于复杂的属性访问很简便。
例如
NSArray * array = @[@{@"key":@{@"1":@"2"},@"key2":@"value2"},
@{@"key":@{@"1":@"3"},@"key2":@"value2"},
@{@"key":@{@"1":@"4"},@"key2":@"value2"}
];
NSLog(@"%@",[array valueForKeyPath:@"key.1"]);
简单讲解下这个过程
对每个对象进行valueForKey:@”key”,然后对返回的数组在进行
valueForKey@“1“
二 KVC的典型使用场景
2.1 Model与View同步的中间件。
我写了个简单的使用Demo
效果如图
每次点Create,会生成100以内随机数,然后几个label分别展示相应的统计。
这里的核心是model和view的同步。
采用如下代码来实现
-(NSArray *)stringKeys{
return @[@"min",@"max",@"avg"];
}
-(NSString *)aggregationKeypath:(NSString *)key{
return [NSString stringWithFormat:@"@%@.self",key];
}
-(UILabel *)labelForKey:(NSString *)key{
return [self valueForKey:key];
}
-(void)updateAggregationLabels{
for (NSString * key in self.stringKeys) {
UILabel * label = [self labelForKey:key];
NSNumber * aggregation = [self.numArray valueForKeyPath:[self aggregationKeypath:key]];
label.text = [NSString stringWithFormat:@"%@: %.1f",key,aggregation.doubleValue];
}
}
- (IBAction)createTenRandomNumbers:(id)sender {
[self.numArray removeAllObjects];
NSString * string = @"";
for (int i = 0; i < 10; i++) {
int num = arc4random()%100;
[self.numArray addObject:@(num)];
string = [NSString stringWithFormat:@"%@ %d",string,num];
}
self.numbers.text = string;
[self updateAggregationLabels];
}
这里,用KVC的方式访问属性。
有些同学不禁会问,我干嘛不直接在这个target-action中直接响应。这么写主要是为了方便以后维护。例如,假如我要新加上一个label叫做count,以上的代码中,我只需要修改一个函数,就是为
-(NSArray *)stringKeys{
return @[@"min",@"max",@"avg"];
}
这个数组添加一个新的key。一个简单的例子,抛砖引玉,主要是尽量解耦合的思想,这个很重要。
2.2 KVC集成了几个集合的运算符,用起来非常方便,代码执行效率也很高。
集合运算符的表达形式就是在keyPath中的双引号里面出现@
例如
@"@min.count"
2.2.1集合运算符分为几种:
- 简单集合运算符:返回string,number,date
- 对象运算符:返回一个数组
- 数组和集合运算符:返回一个数组或者集合
基本集合运算符分为几种
@count
@max
@min
@avg
@sum
为了更好的讲解,写一个model类
@interface User : NSObject
@property (strong,nonatomic)NSString * name;
@property (nonatomic) NSUInteger accessTimes;
-(instancetype)initWithName:(NSString *)name Count:(NSUInteger) times;
@end
然后,进行举例
对一个user的数组进行初始化
-(NSMutableArray *)users{
if (!(_users)) {
_users = [[NSMutableArray alloc] init];
[_users addObject:[[User alloc] initWithName:@"jack" Count:100]];
[_users addObject:[[User alloc] initWithName:@"lucy" Count:150]];
[_users addObject:[[User alloc] initWithName:@"tom" Count:80]];
[_users addObject:[[User alloc] initWithName:@"lily" Count:80]];
}
return _users;
}
然后,用KVC的形式进行访问数据
NSArray * names = [self.users valueForKeyPath:@"name"];
NSArray * counts = [self.users valueForKeyPath:@"accessTimes"];
NSNumber * avg = [self.users valueForKeyPath:@"@avg.accessTimes"];
2.2.2 对象操作符
@unionOfObjects 返回keyPath的结果,重复信息不过滤
@distinucUnionOfObjects 重复信息过滤
例如
NSArray * time1 = [self.users valueForKeyPath:@"@unionOfObjects.accessTimes"];
NSArray * time2 = [self.users valueForKeyPath:@"@distinctUnionOfObjects.accessTimes"];
NSLog(@"%@",time1.description);
NSLog(@"%@",time2.description);
输出
2015-02-12 21:12:38.513 KvcKvoDemo[501:11415] (
100,
150,
80,
80
)
2015-02-12 21:12:38.514 KvcKvoDemo[501:11415] (
150,
80,
100
)
2.3 KVC的一些错误处理
标量不能为nil
例如
@property (nonatomic) int number;
然后
[self setValue:nil forKey:@"number"];
如果不进行错误处理,程序就会崩溃
用这个函数进行想要的错误处理
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{ NSLog(@"undfined key"); }
未识别的key处理
[self setValue:nil forKey:@"dsag"];
[self valueForKey:@"123124"];
胡乱输入的两个key,如果不进行错误处理,程序就会崩溃掉。
用这两个函数来处理未识别的key,至于怎么处理,因情况而定。
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"set undfined key");
}
-(id)valueForUndefinedKey:(NSString *)key{
NSLog(@"get undfined key");
return nil;
}
当然,用消息转发技术,在错误处理的下一步来处理错误也行。这其中又涉及到IOS runtime的内容,牵涉过多,不在本文的讨论范畴了。不过,不要让错误传递太久,及时处理掉,总没错。