詳細文檔,能夠參考官方文檔 KVC官方文檔 developer.apple.com/library/arc… html
//根據key取值 - (id)valueForKey:(NSString *)key; //根據key設值 - (void)setValue:(nullable id)value forKey:(NSString *)key; //根據keyPath取值 - (nullable id)valueForKeyPath:(NSString *)keyPath; //根據keyPath設值 - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; 複製代碼
官方文檔 ios
1.首先查找setter訪問器方法,按照setKey, _setKey, setIsKey順序查找,找到一個就返回,再也不繼續查找. 若是找到訪問器方法,開始檢查參數類型。若是參數類型是指針對象類型,調用方法便可。若是value是非對象指針類型(簡單數值類型或結構體),可是給定的value是nil,則會調用 setNilValueForKey 方法。setNilValueForKey 方法默認實現會拋出NSInvalidArgumentException異常致使程序崩潰,咱們能夠重寫它。 若是找不到訪問器方法,則進行下一步2macos
2.沒找到訪問器方法,則會調用類方法 accessInstanceVariablesDirectly(默認返回YES),若返回YES,則按照順序: _key , _isKey , key, isKey進行查找(找到一個不繼續查找).若是找到了這樣的實例變量,而且其類型是對象指針類型,先release舊對象,再賦值新對象。若是變量類型是其餘類型(基本類型),則先使用NSNumber 或者 NSValue 進行轉換,再進行賦值操做數組
3.若沒找到訪問器方法,也沒有找到實例變量,則會調用 setValue:forUndefinedKey:方法. setValue:forUndefinedKey: 方法默認實現會拋出NSUnknownKeyException異常致使程序崩潰,咱們能夠重寫它bash
備註:這裏官方文檔沒有說起到setIsKey方法(不排除將來會移除該方法),截止目前,該方法依然會被查找!markdown
爲了方便驗證設值流程,咱們不使用@property (不想要系統自動給咱們生成getter/setter訪問器 以及帶下劃線_key成員變量,注意不會生成其餘成員變量) 新建一個Person類app
#import <Foundation/Foundation.h> #import "Address.h" NS_ASSUME_NONNULL_BEGIN @interface Person : NSObject @property (nonatomic, strong) Address * address; @end NS_ASSUME_NONNULL_END 複製代碼
//Person.m實現類 #import "Person.h" @interface Person () { NSString* _name; NSString* name; NSString* _isName; NSString* isName; NSInteger age; NSPoint location; } @end @implementation Person -(void)setIsName:(NSString*)n{ NSLog(@"%s",__func__); isName = n; } -(void)_setName:(NSString*)n{ NSLog(@"%s",__func__); _name = n; } -(void)setName:(NSString*)n{ NSLog(@"%s",__func__); name = n; } - (void)setValue:(id)value forKey:(NSString *)key{ NSLog(@"%s",__func__); [super setValue:value forKey:key]; NSLog(@"%s",__func__); } + (BOOL)accessInstanceVariablesDirectly{ NSLog(@"%s",__func__); return [super accessInstanceVariablesDirectly]; } @end 複製代碼
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person* p = [[Person alloc] init]; //[p setValue:@"JayJay" forKey:@"_name"];方式會直接獲取實例變量 [p setValue:@"JayJay" forKey:@"name"]; } return 0; } 複製代碼
運行斷點調試 oop
能夠看到,優先查找到setName方法,而且給成員變量值name(可隨意修改哪一個成員變量)進行設值 接着將setName訪問器方法註釋掉運行 ui
能夠看到,找到_setName方法,而且給成員變量值_name(可隨意修改哪一個成員變量)進行設值接着將_setName訪問器方法註釋掉運行 編碼
能夠看到,找到setIsName方法,而且給成員變量值isName(可隨意修改哪一個成員變量)進行設值接着註釋掉setIsName方法,再次運行
能夠看到,因爲沒有找到setName,_setName,setIsName方法, [super setValue:value forKey:key] 調用類方法 accessInstanceVariablesDirectly(這裏默認返回YES),而後直接訪問成員變量 _name進行設值接着將成員變量_name註釋掉,再次運行
能夠看到,直接訪問成員變量 _isName進行設值接着將成員變量_isName註釋掉,再次運行
能夠看到,直接訪問成員變量 name進行設值接着將成員變量name註釋掉,再次運行
能夠看到,直接訪問成員變量 isName進行設值接着將 isName註釋,再次運行 程序崩潰,在方法 setValue:forUndefinedKey: 拋出 NSUnknownKeyException異常,咱們能夠重寫該方法(空實現程序就不會崩潰).這裏若是將全部成員變量不註釋,註釋掉setKey,_setKey,setIsKey這三個方法,而重寫類方法 accessInstanceVariablesDirectly,讓其返回NO,也會出現NSUnknownKeyException異常致使崩潰
這裏防止崩潰,咱們重寫setValue: forUndefinedKey 方法
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{ NSLog(@"%s",__func__); } 複製代碼
官方文檔:
1.查找接收者訪問器(這裏是getter)方法,按照 getKey,key ,isKey,_getKey , _key 順序尋找方法,找到一個就返回,再也不繼續查找 若是方法返回結果是對象指針類型,則直接返回,若是結果的類型是NSNumber支持的數值類型之一,則完成轉換並返回NSNumber. 不然,將完成轉換並返回NSValue(Mac OS 10.5中的新增功能:任意類型的結果都將轉換爲NSValues,而不只僅是NSPoint,NSRange,NSRect和NSSize)
2 . 若是沒有找到簡單getter方法,則匹配countOfKey方法和objectInKeyAtIndex:方法 或者 countOfKey方法和keyAtIndexes:方法 若匹配到,則動態建立一個NSArray集合代理對象,該對象響應全部NSArray方法並返回。不然,執行下一步3
備註:動態建立的NSArray代理對象會將NSArray接收到的countOfKey、objectInKeyAtIndex: 、keyAtIndexes:的消息返回給符合KVC規則的調用者.此時NSArray代理對象與上面的方法工做時,系統會將該對象當作NSArray對象,這樣能夠響應全部NSArray方法
3.若是沒有找到NSArray的簡單getter方法,或者NSArray的存取方法組,則查找有沒有countOfKey、enumeratorOfKey、memberOfKey: 命名的方法(三者缺一不可)。若是找到,則動態建立一個NSSet集合代理對象,該對象響應全部NSSet方法並返回。不然,執行下一步4
備註:動態建立的NSSet代理對象會將NSSet接收到的countOfKey、enumeratorOfKey、memberOfKey: 的消息返回給符合KVC規則的調用者.此時NSSet代理對象與上面的方法工做時,系統會將該對象當作NSSet對象,這樣能夠響應全部NSSet方法
4.若是沒有找到簡單getter方法,或集合存取方法組,以及類方法accessInstanceVariablesDirectly返回YES(默認返回YES),則按照_key , _isKey , key, isKey順序進行查找實例,若是找到對應的實例,則馬上獲取實例可用的值並跳轉到第5步,不然,跳轉到第6步.
5.若是取回的是一個指針對象,則直接返回這個結果。若是取回的是簡單值類型,而且能夠被NSNumber或者NSValue轉換,那麼轉換後再返回
6.若是都沒有找到,則會調用 valueForUndefinedKey:方法,會拋出NSUnknownKeyException異常崩潰,能夠重寫該方法。
備註:這裏官方文檔沒有說起到_getKey (不排除將來移除可能性)方法,可是Xcode API點擊進去能夠在最後發現下圖所示提示(Apple從Mac OS 10.3開始不建議使用下劃線開頭的KVC方法),實際截至目前仍然會對此方法進行查找
#import <Foundation/Foundation.h> #import "Address.h" NS_ASSUME_NONNULL_BEGIN @interface Person : NSObject @property (nonatomic, strong) Address * address; @end NS_ASSUME_NONNULL_END 複製代碼
#import "Person.h" @interface Person () { NSString* _name; NSString* name; NSString* _isName; NSString* isName; NSInteger age; NSPoint location; } @end @implementation Person - (instancetype)init { self = [super init]; if (self) { _name = @"_JayJay"; _isName = @"_isJayJay"; name = @"JayJay"; isName = @"isJayJay"; } return self; } -(NSInteger)countOfName{ NSLog(@"%s",__func__); return 3; } //具體能夠參考NSArray裏面 API方法 -(NSString*)objectInNameAtIndex:(NSInteger)index{ NSLog(@"%s",__func__); return @"objectInNameAtIndex"; } - (NSArray<NSString*> *)nameAtIndexes:(NSIndexSet *)indexes{ NSLog(@"%s",__func__); return [@[@"111",@"222",@"333"] objectsAtIndexes:indexes]; } //具體能夠參考NSSet裏面 API方法 -(NSEnumerator<NSString*> *)enumeratorOfName{ NSLog(@"%s",__func__); return [@[@"111",@"222",@"333"] objectEnumerator]; } - (nullable NSString*)memberOfName:(NSString*)object{ NSLog(@"%s",__func__); return @"memberOfName"; } -(NSString*)_name{ NSLog(@"%s",__func__); return _name; } -(NSString*)_getName{ NSLog(@"%s",__func__); return _isName; } -(NSString*)isName{ NSLog(@"%s",__func__); return isName; } -(NSString*)name{ NSLog(@"%s",__func__); return name; } -(NSString*)getName{ NSLog(@"%s",__func__); return _name; } - (id)valueForKey:(NSString *)key{ NSLog(@"%s",__func__); id temp = [super valueForKey:key]; NSLog(@"%s",__func__); return temp; } + (BOOL)accessInstanceVariablesDirectly{ NSLog(@"%s",__func__); return [super accessInstanceVariablesDirectly]; } @end 複製代碼
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person* p = [[Person alloc] init]; //[p valueForKey:@"_name"];方式會直接獲取實例變量 id result = [p valueForKey:@"name"]; NSLog(@"result = %@",result); } return 0; } 複製代碼
斷點調試,首先查找getName方法
註釋掉getName方法,再次運行,會發現去查找name方法 註釋掉name方法,再次運行,會發現去查找isName方法 註釋掉isName方法,再次運行,會發現去查找_getName方法 註釋掉_getName方法,再次運行,會發現去查找_name方法 註釋掉_name方法,再次運行,會發現去優先匹配countOfName和objectInNameAtIndex:方法註釋掉objectInNameAtIndex:方法,再次運行,會發現匹配countOfName和nameAtIndexes:方法
註釋掉objectInNameAtIndex:和 nameAtIndexes:方法,再次運行,會發現匹配countOfName,enumeratorOfName,memberOfName:方法(雖然沒看到調用它,系統內部去處理了,註釋掉這個方法,則會致使匹配失敗)
註釋掉全部簡單getter方法,全部集合存取方法,則會去調用類方法accessInstanceVariablesDirectly(默認返回YES),若返回YES,則按照_name , _isName , name , isName順序取查找,若找到,直接獲取值返回 (相似上面setter過程,這裏就再也不贅述了)
若是上述全部簡單getter方法,全部集合存取方法以及實例變量都沒有找到,則會調用valueForUndefinedKey:方法,拋出NSUnknownKeyException異常崩潰,能夠重寫該方法
-(id)valueForUndefinedKey:(NSString *)key{ NSLog(@"%s",__func__); return NULL; } 複製代碼
/* 默認返回YES,表示若是沒有找到setter或者getter訪問器方法 就會按照_key,_iskey,key,iskey的順序查找實例變量 若是返回NO,則跳過查找實例變量 */ + (BOOL)accessInstanceVariablesDirectly; /* 對屬性值正確性進行驗證,它能夠用來檢查set的值是否正確, 同時爲不正確的值作一個替換值或者拒絕設置新值並返回錯誤緣由 */ - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError; /* 若是屬性是一個NSMutableArray,那麼能夠用這個方法來返回一個集合NSMutableArray 對集合操做的API,裏面還有一系列這樣的API */ - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key; /* 若是沒法搜索到任何和key有關的getter方法或者實例變量 則會調用這個方法,默認會拋出異常 */ - (nullable id)valueForUndefinedKey:(NSString *)key; /* 若是沒法搜索到任何和key有關的setter方法或者實例變量 則會調用這個方法,默認會拋出異常 */ - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; /* 若是在setValue過程當中,給非對象屬性賦值nil,就會調用此方法,默認會拋出異常 */ - (void)setNilValueForKey:(NSString *)key; /* 輸入一組key,返回該組key對應的Value,再轉成字典返回,用於將Model模型轉字典 */ - (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys; /* 輸入一個字典,給調用者對象各個成員變量賦值,用於將字典轉Model模型 */ - (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues; 複製代碼
還有訪問集合屬性的KVC方法:
想象這樣一種情形:一個類的成員變量多是自定義類型或者更復雜類型,咱們可使用KVC來操做這個屬性,而後再次使用KVC對自定義的屬性或者複雜類型進行操做,顯然比較繁瑣.因而蘋果KVC給咱們提供了一個解決方案就是keyPath,就是按路徑遞歸調用(使用「.」號分割)尋找key 來看具體代碼:
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface Address : NSObject @property (nonatomic, copy) NSString* street; @end NS_ASSUME_NONNULL_END #import "Address.h" @implementation Address @end 複製代碼
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc] init]; p.address = [[Address alloc] init]; [p setValue:@"中南路街道" forKeyPath:@"address.street"]; NSLog(@"address.street = %@", [p valueForKeyPath:@"address.street"]); } return 0; } 複製代碼
kvc中主要會出現兩種異常:
這裏NSUnknownKeyException異常上面已經提到過,下面來看NilKey異常
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person* p = [[Person alloc] init]; //這裏age實例變量是NSInteger 簡單數值類型,賦值nil,KVC默認會拋出異常而崩潰 [p setValue:nil forKey:@"age"]; } return 0; } 複製代碼
能夠重寫Person類的setNilValueForKey方法
- (void)setNilValueForKey:(NSString *)key{ NSLog(@"%s",__func__); } 複製代碼
咱們仔細查看-(id)valueForKey:(NSString *)key 知道,方法老是返回一個對象,可是實際應用中徹底可能返回的不是對象。那麼怎麼去處理? 此外- (void)setValue:(id)value forUndefinedKey:(NSString *)key方法中傳入的value也必須是對象類型,那又該怎麼去處理?固然說的這些Apple早就給咱們處理好了。具體就是:對於設值操做,須要咱們本身對非對象數據類型(這裏主要是值類型或者結構體類型)進使用NSNumber 或者 NSValue來包裝.而對於取值操做,Apple已經使用NSNumber 或者 NSValue給咱們封裝好了非對象數據類型,返回的就是NSNumber 或者 NSValue對象
NSNumber支持的值類型有哪些,具體能夠參看Apple 提供的API:
@interface NSNumber (NSNumberCreation) + (NSNumber *)numberWithChar:(char)value; + (NSNumber *)numberWithUnsignedChar:(unsigned char)value; + (NSNumber *)numberWithShort:(short)value; + (NSNumber *)numberWithUnsignedShort:(unsigned short)value; + (NSNumber *)numberWithInt:(int)value; + (NSNumber *)numberWithUnsignedInt:(unsigned int)value; + (NSNumber *)numberWithLong:(long)value; + (NSNumber *)numberWithUnsignedLong:(unsigned long)value; + (NSNumber *)numberWithLongLong:(long long)value; + (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; + (NSNumber *)numberWithFloat:(float)value; + (NSNumber *)numberWithDouble:(double)value; + (NSNumber *)numberWithBool:(BOOL)value; + (NSNumber *)numberWithInteger:(NSInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); + (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); @end 複製代碼
NSValue支持的值類型(macOS 和 iOS有所不一樣,這裏展現的是macOS,具體能夠參看API):
+ (NSValue *)valueWithPoint:(NSPoint)point;
+ (NSValue *)valueWithSize:(NSSize)size;
+ (NSValue *)valueWithRect:(NSRect)rect;
+ (NSValue *)valueWithEdgeInsets:(NSEdgeInsets)insets API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
+ (NSValue *)valueWithRange:(NSRange)range;
複製代碼
來看點代碼:
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc] init]; //傳入的value,是簡單數值類型,須要咱們本身使用NSNumber來封裝(也能夠:[p setValue:@20] forKey:@"age"]) [p setValue:[NSNumber numberWithInt:20] forKey:@"age"]; //傳入的value,是結構體類型,須要咱們本身使用NSValue來封裝 [p setValue:[NSValue valueWithPoint:CGPointMake(100, 100)] forKey:@"location"]; //取值操做,這裏返回的並非NSInteger類型,而是NSNumber類型 id age = [p valueForKey:@"age"]; //取值操做,這裏返回的並非NSPoint類型,而是NSValue類型 id location = [p valueForKey:@"location"]; } return 0; } 複製代碼
KVC提供了對給定的key來對對應的value進行驗證,以確保value是否可用。默認狀況下KVC中不會自動調用,而CoreData在保存託管上下文時會自動驗證(能夠查看Core Data文檔developer.apple.com/library/arc… )。 此外在macOS 中 ,Cocoa Bindings 也會建議您應該自動驗證(能夠查看 Cocoa Bindings相關文檔 developer.apple.com/library/arc… )
Person類重寫下面方法:
-(BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError *__autoreleasing _Nullable *)outError{ NSLog(@"%s",__func__); NSString* value = *ioValue; if ([value isEqualToString:@"JayJay"]) { return NO; } return [super validateValue:ioValue forKey:inKey error:outError]; } 複製代碼
備註:該方法會在內部調用(BOOL)validateKey: error: 也就意味着咱們能夠不須要上面方法,而直接使用它進行單獨驗證!
來看代碼:
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc] init]; NSString*key = @"name"; NSString*value = @"JayJay"; NSError* error; BOOL valid = [p validateValue:&value forKey:key error:&error]; if (valid) { NSLog(@"鍵值匹配"); [p setValue:value forKey:key]; }else{ NSLog(@"鍵值不匹配"); } } return 0; } 複製代碼
再來看如何單獨驗證 註釋掉
Person類中添加方法:
- (BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError{ NSLog(@"%s",__func__); if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 10)) { if (outError != NULL) { *outError = [NSError errorWithDomain:@"PersonErrorDomain" code:110 userInfo:@{ NSLocalizedDescriptionKey : @"姓名過短了!!!" }]; } return NO; } return YES; } 複製代碼
集合運算符分三類:
聚合運算符(Collection operator)有5種: @avg, @count , @max , @min ,@sum
注意:這裏聚合運算符只對NSArray,NSSet操做不包括NSDictionary(NSDictionary 僅僅支持@count操做)
來看點例子:
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, copy) NSString* name; @property (nonatomic, assign) NSInteger age; @end @implementation Person @end int main(int argc, const char * argv[]) { @autoreleasepool { NSMutableArray* pArr = [NSMutableArray array]; for (int i= 0; i< 5; i++) { Person* p = [[Person alloc ] init]; p.age = i + 20; p.name = [NSString stringWithFormat:@"JayJay%d",i]; [pArr addObject:p]; } //@count特殊 NSNumber* count = [pArr valueForKeyPath:@"@count"]; NSLog(@"count: %d",count.intValue); NSNumber* sum = [pArr valueForKeyPath:@"@sum.age"]; NSLog(@"sum: %d",sum.intValue); NSNumber* avg = [pArr valueForKeyPath:@"@avg.age"]; NSLog(@"avg: %d",avg.intValue); NSNumber* min = [pArr valueForKeyPath:@"@min.age"]; NSLog(@"min: %d",min.intValue); NSNumber* max = [pArr valueForKeyPath:@"@max.age"]; NSLog(@"max: %d",max.intValue); } return 0; } 複製代碼
數組運算有以下兩種:
@distinctUnionOfObjects 返回一個去重數組 @unionOfObjects 返回一個不去重數組
注意:若是有一個對象爲nil,就會形成異常崩潰
例子:
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, copy) NSString* name; @property (nonatomic, assign) NSInteger age; @end @implementation Person @end int main(int argc, const char * argv[]) { @autoreleasepool { NSMutableArray* pArr = [NSMutableArray array]; for (int i= 1; i <= 5; i++) { Person* p = [[Person alloc ] init]; p.age = 20 + i%2; p.name = [NSString stringWithFormat:@"JayJay%d",i]; [pArr addObject:p]; } NSArray *distinctAgePersons = [pArr valueForKeyPath:@"@distinctUnionOfObjects.age"]; NSArray *persons = [pArr valueForKeyPath:@"@unionOfObjects.age"]; } return 0; } 複製代碼
這裏操做屬性是age,簡單數值int類型,返回結果數組裏面對應的就是NSNumber類型
有三種操做符: @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets
代碼:
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, copy) NSString* name; @property (nonatomic, assign) NSInteger age; @end @implementation Person @end int main(int argc, const char * argv[]) { @autoreleasepool { NSMutableArray* pArr = [NSMutableArray array]; for (int i= 1; i <= 5; i++) { Person* p = [[Person alloc ] init]; p.age = 20 + i; p.name = [NSString stringWithFormat:@"JayJay%d",i]; [pArr addObject:p]; } NSMutableArray* moreArr = [NSMutableArray array]; for (int i= 1; i <= 5; i++) { Person* p = [[Person alloc ] init]; p.age = 22 + i; p.name = [NSString stringWithFormat:@"More%d",i]; [moreArr addObject:p]; } NSArray* arrayOfArrays = @[pArr, moreArr]; NSArray *collectedDistinctPersons = [arrayOfArrays valueForKeyPath:@"@distinctUnionOfArrays.age"]; NSArray *collectedPersons = [arrayOfArrays valueForKeyPath:@"@unionOfArrays.age"]; } return 0; } 複製代碼
KVC很方便來處理字典和模型之間的轉換,這裏須要注意一點的就是要處理好KVC異常(上面已經提到過),來加強程序健壯性
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys; - (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues; 複製代碼
示例代碼:
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, copy) NSString* name; @property (nonatomic, copy) NSString* sex; @property (nonatomic, strong) NSArray* languages; @property (nonatomic, assign) NSInteger age; @end @implementation Person @end int main(int argc, const char * argv[]) { @autoreleasepool { NSDictionary * dic = @{ @"name":@"JayJay", @"sex":@"男", @"age":@20, @"languages":@[@"C++",@"Objective-c",@"Swift",@"Dart",@"Python"] }; Person* p = [[Person alloc] init]; [p setValuesForKeysWithDictionary:dic]; NSArray* keys = [dic allKeys]; NSDictionary * dict = [p dictionaryWithValuesForKeys:keys]; NSLog(@"dict = %@" ,dict); } return 0; } 複製代碼
寫在最後: 在使用KVC過程當中,因爲傳入的key或者keyPath都是字符串,手動設置或者修改後很容易出錯,從而容易Crash.這裏咱們能夠採用iOS反射機制儘可能避免這個問題.具體作法就是:經過@selector()獲取到方法的SEL,而後經過NSStringFromSelector()將SEL反射爲字符串,這樣在@selector()中傳入方法名的時候,編譯器會有合法性檢查,若是方法不存在或未實現,則會報黃色警告,這樣就能夠減小出錯的機率