iOS-OC KVC KVO
KVO的全称是Key-Value Observing,俗称“键值监听”
作用:可以用于监听某个对象属性值的改变.
被KVO监听的对象会有什么变化???
创建一个类:
@interface MJPerson : NSObject
@property (nonatomic,strong)NSString *age;
@end
然后再viewController中
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [MJPerson new];
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"123"];
[self.person setValue:@"30" forKey:@"age"];
}
在给age赋值的时候会触发如下方法:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSString *oldValue = change[@"old"];
NSString *newValue = change[@"new"];
}
在创建MJPerson
这个类的时候,用KVO
监听之后,runtime会在运行时动态的去创建一个叫NSKVONotifying_MJPerson
的类,它继承自MJPerson类, 它的superClass指针指向MJPerson类对象,同样MJPerson
对象的isa
指针会指向这个类,
如图:
如果我们尝试主动创建NSKVONotifying_MJPerson
,看会有什么情况,结果会报错,告诉你这个类创建不了
2020-05-06 14:54:20.088980+0800 KVO_test[48666:2446594] [general] KVO failed to allocate class pair for name NSKVONotifying_MJPerson, automatic key-value observing will not work for this class
那么KVO的原理是什么???
KVO是通过创建一个中间类,这个类继承自需要监听的类(MJPerson),在set
方法之前调用- (void)willChangeValueForKey:(NSString *)key
,在set
方法之后调用- (void)didChangeValueForKey:(NSString *)key
,并在这个方法里调用- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
,
打印self.person
的类对象(NSKVONotifying_MJPerson
)的方法,类对象-元类有疑问的同学可以看我的上一篇 传送门
通过object_getClass(self.person)
可以拿到类对象(NSKVONotifying_MJPerson
)
Found 4 methods on 'NSKVONotifying_MJPerson'
'NSKVONotifying_MJPerson' has method named 'setAge:' of encoding 'v24@0:8@16'
'NSKVONotifying_MJPerson' has method named 'class' of encoding '#16@0:8'
'NSKVONotifying_MJPerson' has method named 'dealloc' of encoding 'v16@0:8'
'NSKVONotifying_MJPerson' has method named '_isKVOA' of encoding 'B16@0:8'
它自己生成了上述四个方法:
'setAge:' 这里面去调用父类的set方法,从而给_age
赋值
'class' 在你获取它的类的时候[MJPerson class]
给你返回MJPerson
,造成没有中间类的假象
'dealloc' 监听之后的移除操作
'_isKVOA' 标记是否有被监听
问: 怎样主动触发监听方法
答:主动调用- (void)willChangeValueForKey:(NSString *)key
和 - (void)didChangeValueForKey:(NSString *)key
问: NSKVONotifying_MJPerson
它的isa指针指向哪里???
(lldb) po object_getClass(self.person)
NSKVONotifying_MJPerson
(lldb) po object_getClass(object_getClass(self.person))
NSKVONotifying_MJPerson
(lldb) po object_getClass(object_getClass(object_getClass(self.person)))
NSObject
控制台的三次打印是这样,
第一个打印是生成的监听MJPerson的类对象,
第二次打印是生成NSKVONotifying_MJPerson
的元类对象
第三次打印是元类对象最终继承自NSObject
KVC的全称是Key-Value Coding,俗称“键值编码”,
作用:通过一个key来访问某个属性
问: 通过KVC
给某个属性赋值,会触发KVO
吗,
答:会会会,凡是通过set
方法访问的值,都能触发KVO
,前提是KVO必须监听这个属性
KVC的查找机制就是:
image.png第一步就是去查找
setAge
方法,然后_setAge
,_age
,_isAge
,age
,isAge
,顺序找,当你MJPerson属性什么都没有,而你实现了如下方法,则KVC会调用它
- (void)_setAge:(NSString *)age{
// _age = age;
}
注意:如果+ (BOOL)accessInstanceVariablesDirectly
(能否直接访问成员变量 默认YES),返回NO,则找完setAge,_setAge
就报错,setValue: forUndefinedKey
取值原理和赋值原理基本一致.