很多时候我们讲KVO,使用的时候需要注意移除监听,循环引用等问题,但是在自定义KVO的时候,却容易忽视几个问题。
@interface KVOObject : NSObject
@property (strong, nonatomic) NSString *a;
@property (strong, nonatomic) NSString *b;
@property (strong, nonatomic) NSString *c;
@end
@implementation KVOObject
@dynamic c;
- (void)setB:(NSString *)b {
}
- (void)setC:(NSString *)c {
[self willChangeValueForKey:@"c"];
[self didChangeValueForKey:@"c"];
}
- (NSString *)c {
return nil;
}
@end
如果是上述对象,分别对a
,b
,c
监听,其结果是怎么样的呢?
结果是a是正常的,b触发一次,但是其变化的结果都是nil,c触发2次,其结果都是nil。我们来分析下这样的结果吧。
首先,系统KVO的实现是什么?是自动创建一个虚拟对象,并重写set方法,那么这就和这个属性是不是真的存在实现(比如空,或者associate object)无关了。所以当我们再次去调用willChangeValueForKey
和didChangeValueForKey
就产生了2次调用,很多在set方法中再次重申变化是一个错误。
那么我们什么时候需要用到这两个方法呢?当在非set方法里面,通过非set方法变更了属性的值时,就需要手动去触发了。这包括了self->_a
这样的调用。
然后,KVO变化的值是怎么取出来的呢?依据willChangeValueForKey
的接口来看,并没有传入值的参数,那么显而易见,值是从get方法中取出来的。所以如果set和get方法不是对称的,比如- (id)c
所返回的并不是对应c的内容,可能就会出现一些问题,变化的时候并没有触发KVO,触发KVO的时候内容并没有变化。
以上两个在平时碰到的应该比较少,比较自己自定义KVO的场景就比较少。