iOS原理(二)----KVO,KVC
KVO
KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变.下面是一个简单实用KVO的例子:
@interface Animal : NSObject
@property (nonatomic,assign) int age;
@end
@implementation Animal
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.ani = [[Animal alloc] init];
self.ani.age = 10;
[self.ani addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.ani.age = 20;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@---%@---%@",keyPath,object,change);
}
通过断点发现添加KVO后,对象的isa指针变为NSKVONotifying_Animal
,

setAge
实际调用为:NSSetIntValueAndNotify

大概实现为:
- 调用willChangeValueForKey.
- 调用原来的setter实现.
- 调用didChangeValueForKey.
didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法.
KVO动态生成的类,还会重写class
,dealloc
,_isKVOA
等.调用class
方法,会得到原来的类对象,对外封装实现细节.
所以KVO的本质为:利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类,当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数,然后依次调用willChangeValueForKey,父类原来的setter和didChangeValueForKey(内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)).
当对象值没有改变时,我们任然要触发KVO时,我们可以手动触发KVO:手动调用willChangeValueForKey:和didChangeValueForKey.直接修改成员变量的值不会触发KVO.
KVC
KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性
.
setValue:forKey:的原理:
- 按照setKey:、_setKey:顺序查找方法,如果找到方法,传递参数,调用方法.
- 如果没有找到方法,查看accessInstanceVariablesDirectly方法的返回值.
- 如果返回值为NO,调用setValue:forUndefinedKey:并抛出异常NSUnknownKeyException.
- 如果返回值为YES,按照_key、_isKey、key、isKey顺序查找成员变量,如果找到成员变量,就只直接赋值,没有调用setValue:forUndefinedKey:并抛出异常NSUnknownKeyException.
valueForKey:的原理:
- 按照getKey、key、 isKey、_key顺序查找方法,如果找到方法,调用方法.
- 如果没有找到方法,查看accessInstanceVariablesDirectly方法的返回值.
- 如果返回值为NO,调用valueForUndefinedKey:并抛出异常NSUnknownKeyException.
- 如果返回值为YES,按照_key、_isKey、key、isKey顺序查找成员变量,如果找到成员变量,就只直接赋值,没有调用valueForUndefinedKey:并抛出异常NSUnknownKeyException.
KVC会自动触发KVO.
网友评论