KVO
KVO的全称是Key-Value Observing,俗称“键值观察者”,可以用于监听某个对象属性值的改变。
- 当给对象添加KVO时,OC会通过runtime的API动态生成一个继承于该类对象的派生类(NSKVONotifying_对象名),并且使这个interface对象的isa指针指向于这个派生类。
- 当修改interface对象被监听的属性时,调用派生类重写的set方法。
[] set方法中调用Foundation框架中的_NSSet(属性类型)ValueAndNotify函数。在_NSSet(属性类型)ValueAndNotify函数中调用方法顺序如下:
--1. 调用willChangeValueForKey:方法。
--2. 调用父类的set方法。
--3. 调用didChangeValueForKey:方法。该方法内部会调用observer的监听方法(observeValueForKeyPath:ofObject:change:context:)。
补充:
该动态生成的派生类会重写这几个方法:set方法、class方法、dealloc方法、-isKVOA方法。
代码:
- 创建被观察者对象并添加属性:
@interface Person : NSObject
@property(nonatomic, copy)NSString *name;
@end
- 初始化被观察者:
Person *per = [[Person alloc] init];
- 给被观察者添加observe:
/**
*per:被观察者
*observer:观察者
*forKeyPath:被监听的属性
*options:有四个值
1、NSKeyValueObservingOptionNew 把更改之前的值提供给监听方法
2、NSKeyValueObservingOptionOld 把更改之后的值提供给监听方法
3、NSKeyValueObservingOptionInitial 把初始化的值提供给监听方法,一旦 册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
4、NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。
*context:上下文,可以带一些参数,任何类型都可以
*/
[per addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"test"];
- 在观察者中添加监听方法:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"new:%@ old:%@", change[@"new"], change[@"old"]);
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
问题:
- 如何手动触发KVO?
手动调用willChangeValueForKey:和didChangeValueForKey:。 - 直接修改成员变量会触发KVO嘛?
不会触发,因为KVO的本质是触发set方法,成员变量没有set方法。 - 通过KVC修改属性会触发KVO嘛?
会触发,因为KVO的本质是触发set方法。
KVC
KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性。
常见的API有:
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
setValue:(id)value forKey:(NSString *)key的工作原理:

setValue:forKey:的原理
valueForKey:(NSString *)key的工作原理:

valueForKey:的工作原理