KVC定义
- KVC的全称是Key-Value Coding,中文就是我们所熟知的
键值编码
,键值编码是NSKeyValueCoding非正式协议启用的一种机制,通过key值字符串访问对象属性
,这种间接的访问机制补充了实例变量及其相关的访问器方法所提供的直接访问;
KVC的常见API
在Xcode中直接搜索NSKeyValueCoding
,看到NSKeyValueCoding是在Foundation框架中以给目标类添加category的形式实现的,主要目标类为NSObject对象类和存储数据的容器类;
- 最常见的API:
- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
KVC底层实现原理
1. NSObject类 --> setter方法赋值的底层实现
- 在调用setValue: forKey:方法时,其底层实现一般分为下面几个步骤:
- 第一步:目标对象类会按照
setKey
,_setKey
,setIsKey
的顺序去查找set方法,按照顺序只要找到其中一个set方法,就直接去执行赋值操作且不会再往下执行其他的set方法; - 第二步:如果第一步没有找到set方法,那么会去调用
accessInstanceVariablesDirectly
函数;- accessInstanceVariablesDirectly若返回YES(默认返回YES),表明可以去访问实例变量,会按照
_key
,_iskey
,key
,iskey
的顺序搜索实例变量(Key指成员变量),只要找到其中一个,就直接赋值,若都没有找到,则进入第三步; - 若返回NO,表明既没有找到set方法也不能去访问实例变量,则进入第三步;
- accessInstanceVariablesDirectly若返回YES(默认返回YES),表明可以去访问实例变量,会按照
- 第三步:来到这一步说明set方法和实例变量都没有找到,系统会检测该类是否有实现
setValue: forUndefinedKey:
方法,如果该方法没有手动实现,则会抛出NSUndefinedKeyException类型异常;
- 第一步:目标对象类会按照
- 流程图如下所示:
- 代码实现:
@interface YYPerson : NSObject
{
@public
//sex 0-男 / 1-女
//定义四个实例变量
//按照顺序:_key,_isKey,key,isKey查找实例变量
NSString *_sex;
NSString *_isSex;
NSString *sex;
NSString *isSex;
}
@end
@implementation YYPerson
//按照顺序:setKey,_setKey,setIsKey查找set方法
- (void)setSex:(NSString *)value{
NSLog(@"%s - %@", __func__, value);
}
- (void)_setSex:(NSString *)value{
NSLog(@"%s - %@", __func__, value);
}
- (void)setIsSex:(NSString *)value{
NSLog(@"%s - %@", __func__, value);
}
+ (BOOL)accessInstanceVariablesDirectly{
return YES;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"设置出现异常!!!");
}
@end
NSObject类 --> getter方法取值的底层实现
- 在调用valueForKey:方法时,其底层实现一般分为下面几个步骤:
- 第一步:目标对象类会按照
getKey
,key
,isKey
,_key
的顺序去查找get方法,只要找到其中一个get方法,就会立即执行获取到值,且不会再调用后面的get方法; - 第二步:如果第一步没有找到对应的get方法,那么调用
accessInstanceVariablesDirectly
函数- 若返回值为YES,表明可以访问实例变量,会像上面的设值过程一样去查询实例变量
_key
,_isKey
,key
, 如果查询到符合条件的实例变量,会直接取出实例变量的值,反之执行第3步; - 若返回值为NO,表明不可以访问实例变量,直接执行第3步;
- 若返回值为YES,表明可以访问实例变量,会像上面的设值过程一样去查询实例变量
- 第三步:系统会检测该类是否有实现
valueForUndefinedKey:
方法,如果该方法没有实现,则会抛出NSUndefinedKeyException类型异常;
- 第一步:目标对象类会按照
- 流程图如下所示:
@implementation YYPerson
//按照顺序getKey,key,isKey,_key查找get方法
- (NSString *)getSex{
return self->sex;
}
- (NSString *)sex{
return self->sex;
}
- (NSString *)isSex{
return self->isSex;
}
- (NSString *)_sex{
return self->_sex;
}
+ (BOOL)accessInstanceVariablesDirectly{
return NO;
}
- (id)valueForUndefinedKey:(NSString *)key{
NSLog(@"取值出现异常!!!");
return nil;
}
面试题一:KVC设置属性能否触发KVO监听回调
- 测试代码:
- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[YYPerson alloc]init];
self.person1.name = @"a";
[self.person1 addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@" change = %@",change);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.person1 setValue:@"aaa" forKey:@"name"];
}
- 控制台调试结果:
- KVC底层是通过set方法实现的,所以能触发KVO的监听回调函数;
网友评论