OC——关于KVC

我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象。其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Value Coding(简称KVC)。

KVC的操作方法由NSKeyValueCoding协议提供,而NSObject就实现了这个协议,也就是说ObjC中几乎所有的对象都支持KVC操作,常用的KVC操作方法如下:

  • 动态设置: setValue:属性值 forKey:属性名(用于简单路径)setValue:属性值 forKeyPath:属性路径(用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性) 
  • 动态读取: valueForKey:属性名 valueForKeyPath:属性名(用于复合路径)
1 Person *person1=[[Person alloc]init];
2 //动态设置
3 [person1 setValue:@"1111" forKey:@"name"];//简单路径
4 [person1 setValue:@100000000.0 forKeyPath:@"account.balance"];//复合路径
5 
6 //动态读取
7 [person1 valueForKey:@"name" ]//简单路径
8 [person1 valueForKeyPath:@"account.balance"]//复合路径

注:即便对象属性是非字符串你依然可以使用字符串赋值,KVC就是这么霸气。。

从Dictionary来生成对象

setValuesForKeysWithDictionary, 会为我们把和dictionary的key名字相同的class proerty设置上dict中key对应的value,请注意,你的Dictionary的Key一定一定要和你的类的属性名完全一致。

1 -(id) initWithDictionary:(NSMutableDictionary*) jsonObject 
2 { 
3     if((self = [super init])) 
4     { 
5         [self init]; 
6         [self setValuesForKeysWithDictionary:jsonObject]; 
7     } 
8     return self; 
9 } 

如果真的有一些特殊情况使你的Dictionary和你的类的属性肯定有不一样的属性名,那么我们可以重写setValue:(id)value forUndefinedKey:(NSString *)key 方法

1 - (void)setValue:(id)value forUndefinedKey:(NSString *)key 
2 { 
3     if([key isEqualToString:@"nameXXX"]) 
4         self.name = value; 
5     if([key isEqualToString:@"ageXXX"]) 
6         self.age = value; 
7     else 
8         [super setValue:value forKey:key]; 
9 } 

所以只要重载这个方法,就可以处理了那些无法跟property相匹配的key了,默认的实现是抛出一个NSUndefinedKeyException

如果 这时候server返回的People有了内嵌的json(如Products{product1{count:xx, sumPrice:xx}}, product2{} ….),又该怎么办,能把这个内嵌的json转化成我们的客户端的Product类嘛, 当然可以这时候就需要重载setValue:forKey, 单独处理”Products”这个key, 把它wrapper成我们需要的class

 1 -(void) setValue:(id)value forKey:(NSString *)key 
 2 { 
 3   if([key isEqualToString:@"products"]) 
 4   { 
 5     for(NSMutableDictionary *productDict in value) 
 6     { 
 7       Prodcut *product = [[Product alloc] initWithDictionary:prodcutDict]; 
 8       [self.products addObject:product]; 
 9     } 
10   } 
11 } 

一些其他的使用场景

例如我们需要把一个数组里的People的名字的首字母大写,并且把新的名字存入新的数组, 这时候通常做法会是遍历整个数组,然后把每个People的name取出来,调用 capitalizedString 然后把新的String加入新的数组中。 有了KVC就有了新做法:
1 [array valueForKeyPath:@"name.capitalizedString"] 
我们看到valueForKeyPath, 为什么用valueForKeyPath, 不用valueForKey, 因为valueForKeyPath可以传递关系,例如这里是每个People的name property的String的capitalizedString property, 而valueForKey不能传递这样的关系,所以对于dict里面的dict, 我们也只能用valueForKeyPath。这里我们也看到KVC对于array(set), 做了特殊处理,不是简单操作collection上,而是 针对这些collection里面的元素进行操作,同样KVC也提供更多地操作,例如@sum这些针对collection,有兴趣的同学可以去用下。
 
还有一些其他情况,未完待续
原文地址:https://www.cnblogs.com/zhangjingyangjinjin/p/4633879.html