iOS-解析KVO的底层实现
之前讲过KVO的简单应用,这次来讲解下KVO监听属性值的变化到底是怎么实现的,在监听的时候苹果在背后都做了哪些事情。
1.首先我们先来创建一个Person
@interface Person : NSObject
{
@public
NSString * _nickname;
}
@property (nonatomic , copy) NSString *name;
这里需要说下,这里我们用两种方式给Person创建两个属性。我们知道创建name时,会生成相应的setter/getter方法以及_name;创建 _nickname的时候是不会生成setter/getter方法,不明白的请看这里。
在创建_nickname时,如果想要在外部放这个属性需要加在@public将其公开,否则外部放不了这个成员变量,会报错(说成员变量受到了保护)!!
2.我们设置监听
static NSString * nameKey;
static NSString *nicknameKey;
Person *person = [[Person alloc] init];
_person = person;
[_person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:&nameKey];
[_person addObserver:self forKeyPath:@"_nickname" options:NSKeyValueObservingOptionNew context:&nicknameKey];
这里我们对person的两个成员变量name 和 _nickname进行监听,我们来比较下,看看这两种方法创建的属性是不是都能被监听到?
实现:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if (context == &nameKey) {
NSLog(@"name改变了");
}else if (context == &nicknameKey){
NSLog(@"nickname改变了");
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
static int i = 0;
i ++ ;
_person.name = [NSString stringWithFormat:@"name = %d",i];
_person->_nickname = [NSString stringWithFormat:@"nickname = %d",i];
NSLog(@"%@ %@",_person.name,_person->_nickname);
}
当我们touchesBeganw的时候你发现name值的改变被监听到了,而_nickname值的改变没有被监听到,这还是什么原因呢???
这里就要说KVO的到底是怎么实现的了
KVO实现原理
我们先打个断点在这里,调试看看,这里面有什么猫腻
这个时候看你的控制台:
我们看到这时候的person类型还是Person,这个isa指针指向的是真实数据类型。然后我们向下走一步会看到:
咦!person的数据类型变成 NSKVONotifying_Person 了,这是什么情况??难道苹果默默的做了些不为人知的是事情??答案是肯定的。
首先当我们在添加监听的时候,苹果会在内存中默默的为我们创建一个类名字就叫做 NSKVONotifying_XXX ,在这个类里面,会去重写name的setter方法
- (void)setName:(NSString *)name
{
[self willChangeValueForKey:@"name"];// 老值
[super setName:name];
[self didChangeValueForKey:@"name"]; // 新值
}
在setter方法中会执行这个操作,所以我们能够通过 observeValueForKeyPath: 方法收到新值和老值。
回到之前的问题,_nickname属性值变化不能够被监听的原因也就出来了,因为 _nickname没有setter/getter方法,也就没发重写setter方法,最终导致无法监听其值的变化。
以上是我对KVO实现原理的理解,有不对的地方可以评论处回复,我们共同研究!