我们知道在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 }
一些其他的使用场景
1 [array valueForKeyPath:@"name.capitalizedString"]