1、KVC --key-value-coding 键值编码。
KVC是一种存取值得键值编码形式。既然是存取值,那么如何存取就是KVC的关键,这种形式和字典的key-value类似,只是字典是数据类型,KVC是存取对象属性。
KVC的使用:
创建一个Student类,在Student.h文件中声明一个name属性。在Student.m中不做任何处理。
Student.h文件代码:
#import <Foundation/Foundation.h>
@interface Student : NSObject
{
NSString *name;
}
@end
Student.m文件代码:#import "Student.h"
@implementation Student
@end
上面的代码中,在.h文件中没有对name加property,.m文件中也没有实现name属性的读取,这样就不能够使用原来的方法去访问name属性了。现在想要访问就要使用KVC了。main.m文件中使用KVC读写name属性:
#import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc,constchar * argv[]) {
@autoreleasepool {
Student *student = [[Studentalloc]init];
//使用KVC给name属性赋值
[student setValue:@"张三"forKey:@"name"];
//使用KVC读取name属性
NSString *name = [studentvalueForKey:@"name"];
NSLog(@"name:%@", name);
}
return 0;
}
打印结果:2017-03-27 11:03:45.161 OCdemo[887:51191] name:张三
Program ended with exit code: 0
在上面的这个简单的示例中,可以看出KVC其实就是简单的使用setValue:forKey:给对象属性赋值,再使用valueForKey:方法读取对象属性的值。下面来看一段代码:
#import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc,constchar * argv[]) {
@autoreleasepool {
Student *student = [[Studentalloc]init];
//使用KVC给name属性赋值
[student setValue:@"张三"forKey:@"name"];
//使用KVC读取name属性
NSString *name = [studentvalueForKey:@"name"];
NSLog(@"name:%@", name);
//使用KVC给name属性赋值
[student setValue:@"张三"forKey:@"name1"];
//使用KVC读取name属性
NSString *name1 = [studentvalueForKey:@"name1"];
NSLog(@"name:%@", name1);
}
return 0;
}
再次使用KVC给student对象的name1属性赋值、读取,可是Student类中并没有name1这个属性,这个时候系统就会报错:2017-03-27 11:08:57.807 OCdemo[899:52498] name:张三
2017-03-27 11:08:57.810 OCdemo[899:52498] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Student 0x100602a30> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name1.'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff8ff06452 __exceptionPreprocess + 178
1 libobjc.A.dylib 0x00007fff9cb75f7e objc_exception_throw + 48
2 CoreFoundation 0x00007fff8ff06399 -[NSException raise] + 9
3 Foundation 0x00007fff8a339314 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 424
4 OCdemo 0x0000000100000e14 main + 244
5 libdyld.dylib 0x00007fff9dbff5ad start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
错误原因打印的很清楚:this class is not key value coding-compliant for the key name1.
这个类没有name1这个key。所以在写key的时候一定要注意别写错或者写了没有生命的属性。
类的属性可以是很多种多样的,可以是字符串,数组,字典等类型的数据,也可以是一个类的对象。当一个类的对象作为另一个类的属性的时候,如何使用KVC读写呢?
这里先说几个方法:
键值路径读取对象属性
- (nullableid)valueForKeyPath:(NSString *)keyPath;
键值路径存值对象属性
- (void)setValue:(nullableid)value forKeyPath:(NSString *)keyPath;
上面这两个方法就是在类中使用另一个类作为属性时用来读写对象属性的属性的方法。
下面的代码就是Student类中有一个属性是Group类的一个对象,Group类中也有一个属性:groupName:
Group.h文件:
#import <Foundation/Foundation.h>
@interface Group : NSObject
{
NSString *groupName;
}
@end
Group.m文件:#import "Group.h"
@implementation Group
@end
Student.h文件:#import <Foundation/Foundation.h>
@class Group;
@interface Student : NSObject
{
NSString *name;
Group *group;
}
@end
Student.m文件:
#import "Student.h"
@implementation Student
@end
main.m文件:
#import <Foundation/Foundation.h>
#import "Student.h"
#import "Group.h"
int main(int argc,constchar * argv[]) {
@autoreleasepool {
Student *student = [[Studentalloc]init];
//使用KVC给name属性赋值
[student setValue:@"张三"forKey:@"name"];
//使用KVC读取name属性
NSString *name = [studentvalueForKey:@"name"];
NSLog(@"name:%@", name);
Group *group = [[Groupalloc]init];
//使用KVC给group对象的groupName属性赋值
[group setValue:@"一组"forKey:@"groupName"];
//使用KVC给student对象的group属性赋值
[student setValue:groupforKey:@"group"];
//使用valueForKeyPath方法读取groupName属性的值
NSString *groupName = [studentvalueForKeyPath:@"group.groupName"];
NSLog(@"groupName:%@", groupName);
//上面给groupName属性赋值的方式也可以这么写
[student setValue:@"二组"forKeyPath:@"group.groupName"];
//使用valueForKeyPath方法读取groupName属性的值
NSString *groupName1 = [studentvalueForKeyPath:@"group.groupName"];
NSLog(@"groupName:%@", groupName1);
}
return 0;
}
打印结果:
2017-03-27 11:37:49.936 OCdemo[934:62957] name:张三
2017-03-27 11:37:49.937 OCdemo[934:62957] groupName:一组
2017-03-27 11:37:49.937 OCdemo[934:62957] groupName:二组
Program ended with exit code: 0
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;方法的另一种使用情况:
当类的属性是基础数据类型的时候,写入值得时候就要使用上面的键值路径的赋值方法了,但是要注意,在读取的时候使用的是:
- (nullableid)valueForKey:(NSString *)key;这个方法。
给Student类添加一个age属性,类型为NSInteger:
#import <Foundation/Foundation.h>
@class Group;
@interface Student : NSObject
{
NSString *name;
Group *group;
NSInteger age;
}
@end
Student.m文件中代码不变。在main.m文件中读写age属性:
#import <Foundation/Foundation.h>
#import "Student.h"
#import "Group.h"
int main(int argc,constchar * argv[]) {
@autoreleasepool {
Student *student = [[Studentalloc]init];
//使用KVC给name属性赋值
[student setValue:@"张三"forKey:@"name"];
//使用KVC读取name属性
NSString *name = [studentvalueForKey:@"name"];
NSLog(@"name:%@", name);
Group *group = [[Groupalloc]init];
//使用KVC给group对象的groupName属性赋值
[group setValue:@"一组"forKey:@"groupName"];
//使用KVC给student对象的group属性赋值
[student setValue:groupforKey:@"group"];
//使用valueForKeyPath方法读取groupName属性的值
NSString *groupName = [studentvalueForKeyPath:@"group.groupName"];
NSLog(@"groupName:%@", groupName);
//上面给groupName属性赋值的方式也可以这么写
[student setValue:@"二组"forKeyPath:@"group.groupName"];
//使用valueForKeyPath方法读取groupName属性的值
NSString *groupName1 = [studentvalueForKeyPath:@"group.groupName"];
NSLog(@"groupName:%@", groupName1);
//给student对象的age属性赋值
[student setValue:@"20"forKeyPath:@"age"];
//读取age属性
NSString *age = [studentvalueForKey:@"age"];
NSLog(@"age:%@", age);
}
return 0;
}
打印结果:
2017-03-27 11:52:43.951 OCdemo[960:66589] name:张三
2017-03-27 11:52:43.952 OCdemo[960:66589] groupName:一组
2017-03-27 11:52:43.953 OCdemo[960:66589] groupName:二组
2017-03-27 11:52:43.953 OCdemo[960:66589] age:20
Program ended with exit code: 0
在上面的代码中:
//给student对象的age属性赋值
[student setValue:@"20" forKeyPath:@"age"];
//读取age属性
NSString *age = [student valueForKey:@"age"];
NSLog(@"age:%@", age);
这里使用@"20"给age属性赋值,这样在读取的时候就不会有问题,因为都是字符串了。
集合的操作:对象的属性克以是很多的类型,数组,字典这些类型的数据也是可以作为对象属性的,当数组作为属性的时候,又该如何使用KVC读写呢。
假设Student类中有一个数组类型的属性:others,现在我们要打印显示others的name。
代码示例:
Student.h文件:
#import <Foundation/Foundation.h>
@class Group;
@interface Student : NSObject
{
NSString *name;
Group *group;
NSInteger age;
NSArray *others;
}
@end
main.m文件:
#import <Foundation/Foundation.h>
#import "Student.h"
#import "Group.h"
int main(int argc,const char * argv[]) {
@autoreleasepool {
Student *student = [[Studentalloc]init];
//使用KVC给name属性赋值
[student setValue:@"张三"forKey:@"name"];
//使用KVC读取name属性
NSString *name = [studentvalueForKey:@"name"];
NSLog(@"name:%@", name);
Group *group = [[Groupalloc]init];
//使用KVC给group对象的groupName属性赋值
[group setValue:@"一组"forKey:@"groupName"];
//使用KVC给student对象的group属性赋值
[student setValue:groupforKey:@"group"];
//使用valueForKeyPath方法读取groupName属性的值
NSString *groupName = [studentvalueForKeyPath:@"group.groupName"];
NSLog(@"groupName:%@", groupName);
//上面给groupName属性赋值的方式也可以这么写
[student setValue:@"二组"forKeyPath:@"group.groupName"];
//使用valueForKeyPath方法读取groupName属性的值
NSString *groupName1 = [studentvalueForKeyPath:@"group.groupName"];
NSLog(@"groupName:%@", groupName1);
//给student对象的age属性赋值
[student setValue:@"20"forKeyPath:@"age"];
//读取age属性
NSString *age = [studentvalueForKey:@"age"];
NSLog(@"age:%@", age);
//创建对个对象
Student *stu1 = [[Studentalloc]init];
Student *stu2 = [[Studentalloc]init];
//给对象的name赋值
[stu1setValue:@"jack"forKey:@"name"];
[stu2 setValue:@"rose"forKey:@"name"];
//将stu1,stu2天假到数组中
NSArray *array = [NSArrayarrayWithObjects:stu1, stu2,nil];
//给student对象的others属性赋值
[student setValue:arrayforKey:@"others"];
NSLog(@"其他学生的名字:%@", [student valueForKeyPath:@"others.name"]);
}
return 0;
}
打印结果:
2017-03-27 15:28:43.452 OCdemo[1185:121999] name:张三
2017-03-27 15:28:43.454 OCdemo[1185:121999] groupName:一组
2017-03-27 15:28:43.454 OCdemo[1185:121999] groupName:二组
2017-03-27 15:28:43.454 OCdemo[1185:121999] age:20
2017-03-27 15:28:43.455 OCdemo[1185:121999] 其他学生的名字:(
jack,
rose
)
Program ended with exit code: 0
在上面的代码中,可以看到,数组中存放的是一个student对象,然后再使用键值路径的读取形式将每个对象的名字读取出来,在放入数组中。
KVC的使用主要就是对属性的便利读写。在开发中的使用还是比较常见的。而且它是KVO(键值监听)的基础,也就是说KVO是基于KVC的一种设计。在下一篇文章中,将归纳一些KVO的使用。