KVO简介
KVO全称Key-Value-Observing,也就是键值监听,是苹果提供的一套通知机制。它允许一个对象监听另一个对象指定属性值的改变,被观察的对象属性发生改变时,会触发KVO的监听方法通知观察者。
KVO的底层实现
KVO是基于runtime机制,使用isa混写来实现的,一个对象首次被观察时,系统会创建一个派生类来重写setter方法,在setter方法内实现通知机制。被观察的对象的isa会被改变为派生类。当属性发生改变之前,willChangeValueForKeydii:被调用,继而调用didChangeValueForKey:,在此方法中再调用observeValueForKeyPath: ofObject: change: context:
触发方式
1.自动触发
1)调用setter方法
2)使用点语法
3)使用KVC的setValue:forKey:、setValue:ForKeyPath:方法
tips:直接修改成员变量不会触发KVO,因为没有触发setter方法,如:
_name = @"jack";
如果监听集合对象(NSArray、NSSet),需要通过 KVC 的 mutableArrayValueForKey: 或mutableSetValueForKey方法获得代理对象,并修改代理对象,当代理对象的内部对象发生改变时,才会触发 KVO。
例如定义一个Person类如下:
@interface Person : NSObject
@property (nonatomic,copy) NSString * name;
@property (nonatomic,strong) NSMutableArray *maryDatas;
- (void)changeName;
@end
触发场景一,如下代码中data里的数据发生了变化即可触发(指针发生变化不会触发,如data=xxx)
Person *person = [[Person alloc] init];
person.maryDatas = [NSMutableArray arrayWithObject:@"1"];
[person addObserver:self forKeyPath:@"maryDatas" options:NSKeyValueObservingOptionNew context:nil];
NSMutableArray *data = [person mutableArrayValueForKey:@"maryDatas"];
[data addObject:@"2"];// [mary removeLastObject];
触发场景二,调用person的setter方法
Person *person = [[Person alloc] init];
person.maryDatas = [NSMutableArray arrayWithObject:@"1"];
[person addObserver:self forKeyPath:@"maryDatas" options:NSKeyValueObservingOptionNew context:nil];
person.maryDatas = [NSMutableArray arrayWithObjecs:@"2"];
//直接修改maryDatas里的数据不会触发
//[person.maryDatas addObject:@"2"];
2.手动触发
普通对象属性或成员变量手动调用willChangeValueForKey:和didChangeValueForKey:方法
- (void)changeName{
[self willChangeValueForKey:@"name"];
_name = @"jack";
[self didChangeValueForKey:@"name"];
}
NSArray对象调用:
- (void)willChange:(NSKeyValueChange)changeKind valuesAtIndexes:(NSIndexSet *)indexes forKey:(NSString *)key;
- (void)didChange:(NSKeyValueChange)changeKind valuesAtIndexes:(NSIndexSet *)indexes forKey:(NSString *)key;
NSSet对象调用:
- (void)willChangeValueForKey:(NSString *)key withSetMutation:(NSKeyValueSetMutationKind)mutationKind usingObjects:(NSSet *)objects;
- (void)didChangeValueForKey:(NSString *)key withSetMutation:(NSKeyValueSetMutationKind)mutationKind usingObjects:(NSSet *)objects;
3.自动触发控制
重写以下方法可以控制是否自动触发KVO
(1)+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key,可在方法内判断key来决定是否触发,此方法优先级最高。如果我们不希望外界监听name属性,其他属性正常监听,可以在Person类中如下操作:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"name"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
(2)方法名遵循规则+ (BOOL)automaticallyNotifiesObserversOf<Key>(Key为属性名,首字母大写),用来控制某一属性是否触发
+ (BOOL)automaticallyNotifiesObserversOfName {
return NO;
}
+ (BOOL)automaticallyNotifiesObserversOfMaryDatas {
return YES;
}
网友评论