kvc的英文全称是key-value-coding,意思就是键值对编码。用普通的话来说,就是kvc就是通过使用键(字符串)来取值的一种编码。是一种间接访问对象属性的机制,不需要调用getter、setter这些存取方法,就可以直接访问对象属性。
我们声明的变量,有可见度。有些私有的,或者受保护的变量,在其他类中,不能直接使用。我们可以通过kvc,用对象在类外部对实例变量进行访问和赋值。不论是什么可见度的实例变量(private,protected,public)。
setValue: forKey:
setValue:@”1111” forKey:@”name”。以此来举例说明,我们的key为name,此时,对象在调用这个方法的时候,会去找我们的声明中,有没有这个name对应的变量或者属性(名字必须是name或者_name)。否则会导致程序找不到匹配的变量,崩溃。
这个方法,优先调用setter方法,即setXxx这种格式的方法,这个set后的Xxx转换成小写后,就可以作为这个key。
如果这时候,找不到匹配的setter方法,就会去找实例变量,并且自动匹配下划线 _ 。(我们在key中加不加下划线 _ 都可以)。如果两者都找不到,就会崩溃。
Person.h
#import <Foundation/Foundation.h> @interface Person : NSObject{ @private NSString *_name; NSString *_sex; } - (void)sayHi; @end |
Person.m
#import "Person.h" @implementation Person - (void)sayHi{ NSLog(@"%@",_name); } @end |
main.m
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p1 = [[Person alloc]init]; //setValue:forKey: //优先调用setter方法,即:setXxx:,这个set后面的 Xxx 就可以作为key。 //其次,set方法没有找到,就去找实例变量。并且自动识别下划线。(我们在key里加不加下划线都可以识别名字)。如果名字不一样,崩溃 p1->_name; //kvc第一个方法: [p1 setValue:@"123" forKey:@"name"]; NSLog(@"%@",[p1 valueForKey:@"name"]); } return 0; } |
解释:
[p1 setValue:@"123" forKey:@"name"];p1在设置key为name的变量的值为“123”,此时就回去找setter方法,发现没有定义setter方法,就回去实例变量中匹配,发现有一个_name,与之匹配,则对 _name进行赋值。
valueForKey:用键取值
通过键来取value,优先调用getter方法,取值,即形如我们定义的getter方法的方法xxx,((返回值)方法名,这样的方法就是形如getter方法。方法名就作为key),如实没有找到getter方法,就会去找实例变量,并能识别下划线 _。
NSLog(@"%@",[p1 valueForKey:@"name"]); |
解释:去找name的getter方法,发现没找到,就去找实例变量,匹配到了,就可以取到值。
valueForKey:不仅可以取到变量值,还能用方法名作为key去找一个方法,然后执行。
Person.h
#import <Foundation/Foundation.h> @interface Person : NSObject{ @private NSString *_name; NSString *_sex; } - (void)sayHi; @end |
Person.m
#import "Person.h" @implementation Person - (void)sayHi{ NSLog(@"%@",_name); } - (void)setLol:(NSString *)lol{ NSLog(@"%@",lol); } //没有找到对应key - (void)setValue:(id)value forUndefinedKey:(NSString *)key{ NSLog(@"没有key:%@,value:%@",key,value); } - (id)valueForUndefinedKey:(NSString *)key{ NSLog(@"没有 key :%@",key); return nil; } @end |
main.m
[p1 sayHi]; [p1 valueForKey:@"sayHi"]; [p1 setValue:@"德玛西亚" forKey:@"Lol"];//2015-04-22 16:08:00.048 OClesson8_KVC[2113:121073] 德玛西亚 |
解释:
在.h中声明了sayHi方法,在.m中实现sayHi,此时在main函数中用p1对象调用valueForKey:@"sayHi",就会把这个sayHi字符串去匹配,发现匹配了一个方法,此时就会去调用这个方法,执行。
在.m中实现了Lol:这个方法,在main方法中,对象p1调用了setValue:@"德玛西亚" forKey:@"Lol"方法,就把Lol作为key去匹配,发现匹配了setLol:方法,此时就会把value当作参数传进去,执行setLol:方法。
从父类继承过来的私有,受保护的实例变量,也可以用kvc访问。
setValue: forKeyPath:按照路径设置值
valueForKeyPath: 按照路径取值
若实例变量中有我们自定义的类,这个自定义的类中有另外的实例变量。此时要想访问实例变量为自定义类中的实例变量,就需要kvc按路径访问。
setValue:forUndefinedKey:设置值匹配不到key的处理
valueForUndefinedKey: 取值匹配不到key的处理
如果在按key访问变量或方法的时候,匹配不到所给的字符串(也就是key),需要加上setValue:forUndefinedKey(设置值)或者valueForUndefinedKey(取值)进行相应的处理,防止程序崩掉。
setValuesForKeysWithDictionary:按字典设置值
当有很多个值得时候,我们可以一次性赋值,就是用字典来赋值。
Person.h(父类)
#import <Foundation/Foundation.h> @interface Person : NSObject{ @private NSString *_name; NSString *_sex; } - (void)sayHi; @end |
Person.m(父类实现)
#import "Person.h" @implementation Person - (void)sayHi{ NSLog(@"%@",_name); } - (void)setLol:(NSString *)lol{ NSLog(@"%@",lol); } //没有找到对应key - (void)setValue:(id)value forUndefinedKey:(NSString *)key{ NSLog(@"没有key:%@,value:%@",key,value); } - (id)valueForUndefinedKey:(NSString *)key{ NSLog(@"没有 key :%@",key); return nil; } @end |
Student.h(继承于Person类)
#import "Person.h" @class Book; @interface Student : Person{ Book *_book; } @end |
Student.m
#import "Student.h" #import "Book.h" @implementation Student @end |
Book.h(继承于Student类)
#import <Foundation/Foundation.h> @interface Book : NSObject{ NSString *_bookName; } @end |
Book.m
#import "Book.h" @implementation Book @end |
main.m
#import <Foundation/Foundation.h> #import "Person.h" #import "Student.h" #import "Book.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p1 = [[Person alloc]init]; Student *s1 = [[Student alloc]init]; //从父类继承的私有实例变量,也可以用kvc访问。 [s1 setValue:@"22222" forKey:@"name"]; NSLog(@"%@",[s1 valueForKey:@"name"]);//2015-04-22 16:21:33.162 OClesson8_KVC[2186:125614] 22222 Student *s2 = [[Student alloc]init]; Book *b1 = [[Book alloc]init]; // [b1 setValue:@"红楼梦" forKey:@"bookName"]; [s2 setValue:b1 forKey:@"book"]; [s2 setValue:@"红楼梦" forKeyPath:@"book.bookName"];//点 . 不是点语法,是路径的意思。 NSLog(@"%@",[s2 valueForKeyPath:@"book.bookName"]);//2015-04-22 16:32:49.975 OClesson8_KVC[2236:129639] 红楼梦 // //设置值没有对应的key,在person.m中写一个undifind // [p1 setValue:@"12131313" forKeyPath:@"num"];//2015-04-22 16:54:24.955 OClesson8_KVC[2324:135915] 没有key:num,value:12131313 // //取值没有对应的key,在person.m中写一个undifind // [p1 valueForKeyPath:@"num"];//2015-04-22 16:56:22.108 OClesson8_KVC[2348:136844] 没有 key :num NSDictionary *dict = @{@"name":@"Mick",@"sex":@"男"}; //一次性赋多个值,在网络解析中常用。 [p1 setValuesForKeysWithDictionary:dict]; } return 0; } |