KVC(Key-value coding)
鍵值編碼
基本使用
- 能夠?qū)ο蟮乃接谐蓡T進行取值賦值
- 對數(shù)值和結(jié)構(gòu)體型的屬性進行的打包解包處理
實例: WTPerson.h
#import <Foundation/Foundation.h>
@interface WTPerson : NSObject{
// @public //@protect默認
NSString * _name;
}
/** name **/
//@property(nonatomic,strong)NSString * name;
@end
ViewController.m
#import "ViewController.h"
#import "WTPerson.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *text;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WTPerson * p = [WTPerson new];
//訪問成員變量
//p.name = @"wt";
//NSLog(@"%@",p.name);
//訪問私有變量(必須要要設(shè)置為public才可訪問)
//p->_name = @"wt";
//NSLog(@"%@",p->_name);
//KVC(即使不用public修飾,也可以訪問私有變量)
[p setValue:@"wt" forKey:@"name"];
NSLog(@"%@",[p valueForKey:@"name"]);
[self.text setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
}
作為一個開發(fā)者憨栽,有一個學習的氛圍跟一個交流圈子特別重要谈喳,這是一個我的iOS交流群:642 363 427不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經(jīng)驗愧驱,討論技術(shù)浙滤, 大家一起交流學習成長!
KVC賦值取值過程分析和自定義及異常處理
賦值過程
- 1彰檬、先找相關(guān)方法
set<Key>; _set<Key>; setIs<Key>;
- 2伸刃、若是沒有相關(guān)方法
+(BOOL)accessInstanceVariablesDirectly
判斷是否可以直接訪問成員變量 - 3、如果判斷NO逢倍,直接執(zhí)行KVC的
setValue:forUndefinedKey:(系統(tǒng)拋出一個異常捧颅,未定義key)
- 4、如果是YES较雕,繼續(xù)找相關(guān)變量
_<key> _is<Key> <key> is<Key>
- 5碉哑、方法或成員都不存在,
setValue:forUndefinedKey:
方法默認是拋出異常
實例驗證
WTPerson.h
#import <Foundation/Foundation.h>
@interface WTPerson : NSObject{
@public //@protect默認
NSString * _name;
NSString * _isName;
NSString * name;
NSString * isName;
}
@end
WTPerson.m
#import "WTPerson.h"
@implementation WTPerson
-(void)setName:(NSString *)name{
NSLog(@"%s",__func__);
}
-(void)_setName:(NSString *)name{
NSLog(@"%s",__func__);
}
-(void)setIsName:(NSString *)name{
NSLog(@"%s",__func__);
}
@end
ViewController.m
#import "ViewController.h"
#import "WTPerson.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WTPerson * p = [WTPerson new];
//驗證KVC賦值過程
[p setValue:@"wt" forKey:@"name"];
NSLog(@"name = %@",p->name);
NSLog(@"_name = %@",p->_name);
NSLog(@"isname = %@",p->isName);
NSLog(@"_isname = %@",p->_isName);
}
@end
- 運行程序亮蒋,我們把
WTPerson.m
中的-(void)setName:(NSString *)name
扣典、-(void)_setName:(NSString *)name
、-(void)setIsName:(NSString *)name
三個方法依次注釋慎玖,我們發(fā)現(xiàn)三個方法都會被依次執(zhí)行贮尖。 - 然后我們把
WTPerson.h
中的NSString * _name;
、NSString * _isName;
趁怔、NSString * name;
湿硝、NSString * isName;
依次注釋,我們會發(fā)現(xiàn)4個屬性依次被賦值润努。
在WTPerson.m
中我們讓accessInstanceVariablesDirectly
返回NO
关斜,則程序直接崩潰。
+ (BOOL)accessInstanceVariablesDirectly{
return NO;
}
取值過程
- 1铺浇、先找相關(guān)方法
get<Key>,key
- 2痢畜、若沒有相關(guān)方法,
+(BOOL)accessInstanceVariabkesDirectly
判斷是否可以直接訪問成員變量 - 3鳍侣、如果是NO丁稀,直接執(zhí)行KVC的
valueForUndefinedKey:
(系統(tǒng)拋出一個異常,未定義key) - 4倚聚、如果是YES二驰,繼續(xù)找相關(guān)變量
_<key>、_is<Key>秉沼、<key>、is<Key>
- 5矿酵、方法或成員都不存在唬复,
valueForUndefineKey:
方法,默認是拋出異常
實例驗證
WTPerson.m
#import "WTPerson.h"
@implementation WTPerson
//- (NSString*) getName{
// NSLog(@"%s",__func__);
// return @"getName";
//}
- (NSString*) name {
return @"name";
}
//+ (BOOL)accessInstanceVariablesDirectly{
// return NO;
//}
@end
ViewController.m
#import "ViewController.h"
#import "WTPerson.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *text;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WTPerson * p = [WTPerson new];
//驗證KVC取值過程
NSLog(@"name = %@",[p valueForKey:@"name"]);
}
@end
取值方式與賦值方式大致相同全肮。
KVC自定義
自定義KVC代碼實現(xiàn)
創(chuàng)建分類NSObject+KVC
NSObject+KVC.h
#import <Foundation/Foundation.h>
@interface NSObject (KVC)
- (void)wt_setValue:(nullable id)value forKey:(NSString *)key;
- (id)wt_valueForKey:(NSString *)key;
@end
NSObject+KVC.m
#import "NSObject+KVC.h"
#import <objc/runtime.h>
@implementation NSObject (KVC)
- (id)wt_valueForKey:(NSString *)key{
//判斷是否合法
if (key == nil && key.length ==0) {
return nil;
}
//Key
NSString * Key = key.capitalizedString;
//先找相關(guān)方法 get<Key>,key
NSString * getKey = [NSString stringWithFormat:@"get%@:",Key];
if ([self respondsToSelector:NSSelectorFromString(getKey)]) {
return [self performSelector:NSSelectorFromString(getKey)];
}
if ([self respondsToSelector:NSSelectorFromString(key)]) {
return [self performSelector:NSSelectorFromString(key)];
}
if (![self.class accessInstanceVariablesDirectly]) {
NSException * exception = [NSException exceptionWithName:@"NSUnknownKeyException" reason:@"setValue:forUndefineKey" userInfo:nil];
@throw exception;
}
//再找相關(guān)變量
//獲取所有的成員變量
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([self class], &count);
NSMutableArray * arr = [[NSMutableArray alloc]init];
for (int i = 0; i<count; i++) {
Ivar var = ivars[i];
const char * varName = ivar_getName(var);
NSString *name = [NSString stringWithUTF8String:varName];
[arr addObject:name];
}
//_<key> _is<Key> <key> is<Key>
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) {
return object_getIvar(self, ivars[i]);
}
}
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",Key]]) {
return object_getIvar(self, ivars[i]);
}
}
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"%@",key]]) {
return object_getIvar(self, ivars[i]);
}
}
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",Key]]) {
return object_getIvar(self, ivars[i]);
}
}
free(ivars);
return nil;
}
- (void)wt_setValue:(nullable id)value forKey:(NSString *)key{
//判斷是否合法
if (key == nil && key.length ==0) {
return;
}
//Key
NSString * Key = key.capitalizedString;
//先找相關(guān)方法 set<Key>; _set<Key>; setIs<Key>;
NSString * setKey = [NSString stringWithFormat:@"set%@:",Key];
if ([self respondsToSelector:NSSelectorFromString(setKey)]) {
[self performSelector:NSSelectorFromString(setKey) withObject:value];
return;
}
NSString * _setKey = [NSString stringWithFormat:@"_set%@:",Key];
if ([self respondsToSelector:NSSelectorFromString(_setKey)]) {
[self performSelector:NSSelectorFromString(_setKey) withObject:value];
return;
}
NSString * setIsKey = [NSString stringWithFormat:@"setIs%@:",Key];
if ([self respondsToSelector:NSSelectorFromString(setIsKey)]) {
[self performSelector:NSSelectorFromString(setIsKey) withObject:value];
return;
}
if (![self.class accessInstanceVariablesDirectly]) {
NSException * exception = [NSException exceptionWithName:@"NSUnknownKeyException" reason:@"setValue:forUndefineKey" userInfo:nil];
@throw exception;
}
//再找相關(guān)變量
//獲取所有的成員變量
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([self class], &count);
NSMutableArray * arr = [[NSMutableArray alloc]init];
for (int i = 0; i<count; i++) {
Ivar var = ivars[i];
const char * varName = ivar_getName(var);
NSString *name = [NSString stringWithUTF8String:varName];
[arr addObject:name];
}
//_<key> _is<Key> <key> is<Key>
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) {
object_setIvar(self, ivars[i], value);
free(ivars);
return;
}
}
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",Key]]) {
object_setIvar(self, ivars[i], value);
free(ivars);
return;
}
}
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"%@",key]]) {
object_setIvar(self, ivars[i], value);
free(ivars);
return;
}
}
for (int i = 0; i < count; i++) {
NSString *keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",Key]]) {
object_setIvar(self, ivars[i], value);
free(ivars);
return;
}
}
[self setValue:value forUndefinedKey:Key];
free(ivars);
}
@end
驗證
ViewController.m
#import "ViewController.h"
#import "WTPerson.h"
#import "NSObject+KVC.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WTPerson * p =[WTPerson new];
[p wt_setValue:@"wt" forKey:@"name"];
NSLog(@"name-KVC = %@",[p wt_valueForKey:@"name"]);
NSLog(@"_name = %@",p->_name);
NSLog(@"_isName = %@",p->_isName);
NSLog(@"name = %@",p->name);
NSLog(@"isName = %@",p->isName);
}
@end
在項目中 commond+shift+o
搜索setValue:forKey
發(fā)現(xiàn)在Foundation
框架下的NSKeyValueCoding
文件下
ht3bbp1s51 (1).png
我們查看這個文件中的方法敞咧,發(fā)現(xiàn)這個文件中是一些分類的集合
zsormyk4s5 (1).png
KVC異常處理及正確性驗證
KVC異常處理
- 1、賦值為空
setNilValueForKey
- 2辜腺、Key值不存在
setValue:forUndefinedKey
正確性驗證
validateValue 該方法的工作原理:
- 1休建、先找一下你的類中是否實現(xiàn)了方法
-(BOOL)validate<Key>:error;
- 2乍恐、如果實現(xiàn)了就會根據(jù)實現(xiàn)方法里面的自定義邏輯返回NO或者YES;如果沒有實現(xiàn)這個方法测砂,則系統(tǒng)默認返回YES
示例代碼
WTPerson…h(huán)
#import <Foundation/Foundation.h>
@interface WTPerson : NSObject
/** name **/
@property(nonatomic,strong)NSString * name;
/** age **/
@property(nonatomic,assign)int age;
@end
WTPerson.m
#import "WTPerson.h"
@implementation WTPerson
//對非對象類型茵烈,值不能為空
- (void) setNilValueForKey:(NSString *)key{
NSLog(@"%@ 值不能為空",key);
}
//賦值的key不存在
- (void) setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"key = %@值不存在",key);
}
//取值的key不存在
- (id) valueForUndefinedKey:(NSString *)key{
NSLog(@"key = %@值不存在",key);
return nil;
}
//正確性驗證
- (BOOL) validateAge:(inout id _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError{
NSNumber* value = (NSNumber*)*ioValue;
NSLog(@"%@",value);
if ([value integerValue] >= 0 && [value integerValue] <= 200) {
return YES;
}
return NO;
}
@end
ViewController.m
#import "ViewController.h"
#import "WTPerson.h"
#import "WTContainer.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WTPerson * p = [WTPerson new];
//異常處理
[p setValue:@18 forKey:@"name"];
[p setValue:nil forKey:@"name"];
NSLog(@"name = %@",p.name);
[p setValue:nil forKey:@"age"];
NSLog(@"age = %d",p.age);
[p setValue:@"hello" forKey:@"name1"];
NSLog(@"name = %@",[p valueForKey:@"name1"]);
//萬能容器
WTContainer * container = [WTContainer new];
[container setValue:@"wt" forKey:@"name"];
[container setValue:@18 forKey:@"age"];
NSLog(@"name = %@,age = %@",[container valueForKey:@"name"],[container valueForKey:@"age"]);
//正確性驗證
NSNumber * value = @200;
NSNumber * value1 = @199;
if ([p validateValue:&value1 forKey:@"age" error:NULL]) {
[p setValue:value1 forKey:@"age"];
}
NSLog(@"%@",[p valueForKey:@"age"]);
}
@end
原文:GitHub