前言
最近在寫關(guān)于KVC、KVO的一些東西,也許很多人都認(rèn)為KVC再簡(jiǎn)單不過(guò)了,其實(shí)不是這個(gè)樣子的,因?yàn)槲乙郧案蟛糠秩说南敕ㄊ且粯拥模琄VC、KVO固定的書寫模式,調(diào)用也超級(jí)簡(jiǎn)單。其實(shí)不是的,因?yàn)樵绞呛?jiǎn)單的東西,越是容易被人忽略。現(xiàn)在就給大家分享一些關(guān)于KVC你可能不知道的東西。
KVC的賦值原理
setValue:forKey:賦值原理如下:
- 去模型中查找有沒(méi)有對(duì)應(yīng)的setter方法:例如:setIcon方法,有就直接調(diào)用這個(gè)setter方法給模型這個(gè)屬性賦值[self setIcon:dic[@"icon"]];
- 如果找不到setter方法,接著就會(huì)去尋找有沒(méi)有icon屬性,如果有,就直接訪問(wèn)模型中的icon屬性,進(jìn)行賦值,icon=dict[@"icon"];
- 如果找不到icon屬性,接著又會(huì)去尋找_icon屬性,如果有,直接進(jìn)行賦值_icon=dict[@"icon"];
- 如果都找不到就會(huì)報(bào)錯(cuò):[<Flag 0X7fb74bc7a2c0> setValue:forUndefinedKey:]
擴(kuò)展
讀者可以去查查KVV(鍵值驗(yàn)證),進(jìn)一步理解報(bào)錯(cuò)原因與容錯(cuò)方法。
KVC開(kāi)關(guān)及檢查
-
如果對(duì)某個(gè)類,不允許使用KVC,可以通過(guò)設(shè)置 accessInstanceVariablesDirectly 控制。
// 在該類的內(nèi)部,重寫此方法,外部使用KVC時(shí),禁用沒(méi)有寫set get 方法的屬性值。 // 注意:對(duì)于 @property 定義的屬性可以 KVC+ -(BOOL)accessInstanceVariablesDirectly{ return NO; }
賦值檢查
// 在類的內(nèi)部,進(jìn)行檢查,不符合要求 返回NO ,提供外部參考。
- (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable __autoreleasing )outError{
if ([inKey isEqualToString:@"colors"] && [ioValue isKindOfClass:[NSArray class]]) {
return YES;
} else {
return NO;
}
}
//用法:
// 外部 使用時(shí),先判斷是否符合要求,再使用KVC。
NSError *error;
NSString *apoint = @"name";
if ([aPerson validateValue:&apoint forKey:@"_colors" error:&error]) {
NSLog(@"可以賦值 apoint");
[aPerson setValue:apoint forKey:@"_colors"];
} else {
NSLog(@"不可以賦值 apoint");
NSLog(@"%@",error.debugDescription);
}
KVC通過(guò)Dictionary為模型賦值(與昨天的有極大的差別)
昨天:昨天的情況,后臺(tái)返回的json與模型變量名一致。
今天:如果后臺(tái)給我們返回的一些json字符串,與我們的model屬性一部分不一致(或者json包含關(guān)鍵字)。
簡(jiǎn)單的說(shuō):如果json里面的某些key就是和object的property名字不一樣呢,或者有些server返回的字段是objc保留字如”id”, “description”等, 我們也希望也map dict to object, 這時(shí)候我們就需要用上setValue:forUndefinedKey, 因?yàn)槿绻覀儾惶幚磉@些Undefined Key,還是用setValuesForKeysWithDictionary就會(huì) 拋出異常。
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
if([key isEqualToString:@"nameXXX"])
self.name = value;
if([key isEqualToString:@"ageXXX"]) {
self.age = value;
}else{
[super setValue:value forKey:key];
}
}
所以只要重載這個(gè)方法,就可以處理了那些無(wú)法跟property相匹配的key了,默認(rèn)的實(shí)現(xiàn)是拋出一個(gè)NSUndefinedKeyException。
KVC處理包含類內(nèi)部包含類的情況(重寫setValue:forKey:)
如果這時(shí)候server返回的People有了內(nèi)嵌的json(如Products{product1{count:xx, sumPrice:xx}}, product2{} ….),又該怎么辦,能把這個(gè)內(nèi)嵌的json轉(zhuǎn)化成我們的客戶端的Product類嘛, 當(dāng)然可以這時(shí)候就需要重載setValue:forKey, 單獨(dú)處理”Products”這個(gè)key, 把它wrapper成我們需要的class
-(void) setValue:(id)value forKey:(NSString *)key
{
if([key isEqualToString:@"products"])
{
for(NSMutableDictionary *productDict in value)
{
Prodcut *product = [[Product alloc] initWithDictionary:prodcutDict];
[self.products addObject:product];
}
}
}
從這一點(diǎn)中可以知道,setValuesForKeysWithDictionary內(nèi)部調(diào)用了setValue:forKey:方法。
keyPath的巧妙用法
情景:我們需要把一個(gè)數(shù)組里的People的名字的首字母大寫,并且把新的名字存入新的數(shù)組, 這時(shí)候通常做法會(huì)是遍歷整個(gè)數(shù)組,然后把每個(gè)People的name取出來(lái),調(diào)用 capitalizedString 然后把新的String加入新的數(shù)組中。 有了KVC就有了新做法:
[array valueForKeyPath:@"name.capitalizedString"]
疑問(wèn):為什么用valueForKeyPath, 不用valueForKey?
因?yàn)?/strong>:valueForKeyPath可以傳遞關(guān)系,例如這里是每個(gè)People的name property的String的capitalizedString property, 而valueForKey不能傳遞這樣的關(guān)系,所以對(duì)于dict里面的dict, 我們也只能用valueForKeyPath。
KVC的一些其他用法
FOUNDATION_EXPORT NSString *const NSUndefinedKeyException;
FOUNDATION_EXPORT NSString *const NSAverageKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSCountKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSDistinctUnionOfArraysKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSDistinctUnionOfObjectsKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSDistinctUnionOfSetsKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSMaximumKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSMinimumKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSSumKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSUnionOfArraysKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSUnionOfObjectsKeyValueOperator;
FOUNDATION_EXPORT NSString *const NSUnionOfSetsKeyValueOperator;
@interface NSObject(NSKeyValueCoding)
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError **)outError;
- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath NS_AVAILABLE(10_7, 5_0);
- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
@end
@interface NSArray<ObjectType>(NSKeyValueCoding)
- (id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
@end
@interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)
- (nullable ObjectType)valueForKey:(NSString *)key;
@end
@interface NSMutableDictionary<KeyType, ObjectType>(NSKeyValueCoding)
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
@end
@interface NSOrderedSet<ObjectType>(NSKeyValueCoding)
- (id)valueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);- (void)setValue:(nullable id)value forKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);
@end
@interface NSSet<ObjectType>(NSKeyValueCoding)
- (id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
@end
未完待續(xù)……
歡迎關(guān)注我的個(gè)人微信公眾號(hào),免費(fèi)送計(jì)算機(jī)各種最新視頻資源!你想象不到的精彩!