1.KVO
KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变
KVO本质是利用RuntimeAPI动态生成一个子类,并且让这个instance对象的isa指向这个全新的子类,当修改instance对象的属性时,会调用Foundtion的_NSSetXXXValueAndNotify函数,该函数包含三部分
willChangeValueForKey:
super 父类的setter方法
didChangeValueForKey:
在didChangeValueForKey函数内部会触发监听器(observer)的监听方法(observeValueForKeyPath: ofObject:change: context:)
以下是生成的子类中的伪代码:
#import "NSKVONotifying_MJPerson.h"
@implementation NSKVONotifying_MJPerson
- (void)setAge:(int)age
{
_NSSetIntValueAndNotify();
}
// 伪代码
void _NSSetIntValueAndNotify()
{
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
- (void)didChangeValueForKey:(NSString *)key
{
// 通知监听器,某某属性值发生了改变
[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
监听器可以手动触发KVO,
[xxx willChangeValueForKey:@"key"];
[xxx didChangeValueForKey:@"key"];
的时候就会触发,当然这里值没有改变。
直接修改成员变量不会触发KVO
如
person->_age = 10;
不会触发
可以配合手动触发实现值改变,也就是_NSSet*ValueAndNotify的内部实现
[person willChangeValueForKey:@"age"];
perosn->_age = 10;
[person didChangeValueForKey:@"age"];
/*--------------------------------------------------------------------验证NSKVONotifying_XXX子类的存在-------------------------------------------*/
//注:person1进行值监听,person2不进行值监听
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
NSLog(@"person1添加KVO监听之后 - %@ %@",
object_getClass(self.person1),
object_getClass(self.person2));
NSLog(@"person1添加KVO监听之后 - %p %p",
[self.person1 methodForSelector:@selector(setAge:)],
[self.person2 methodForSelector:@selector(setAge:)]);
会发现person1的类对象是NSKVONotifying_MJPerson,方法打印是 (Foundation`_NSSetIntValueAndNotify),而person2的类对象依然是MJPerson,方法打印是(Interview01`-[MJPerson setAge:] at MJPerson.m:13)
但是如果用[self.person1 class]调用会发现打印的类对象是Person,这里猜测是在NSKVONotifying_MJPerson中重写了class方法,屏幕内部实现,隐藏了NSKVONotifying_MJPerson类的存在
2.KVC
KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
1.KVC赋值原理
其中+ (BOOL)accessInstanceVariablesDirectly表示是否允许直接修改成员变量的值,默认是YES
可以看到调用set方法的时候是按照setKey和_setKey的顺序调用的,给成员变量赋值的时候是按照_key、_isKey、key、isKey的顺序赋值的,就算同时存在并且顺序打乱也是按照这个顺序赋值。
2.KVC取值原理
调用顺序也是固定死的。
KVC可以触发KVO!!
[person setValue:@10 forKey:@"age"];
内部实现其实是
[person willChangeValueForKey:@"age"];
person->_age = 10;
[person didChangeValueForKey:@"age"];