我们知道在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"]//复合路径
从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,有兴趣的同学可以去用下。
还有一些其他情况,未完待续