iOS-OC KVC KVO

2020-05-06  本文已影响0人  洧中苇_4187

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指针会指向这个类,
如图:

image.png

如果我们尝试主动创建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

取值原理和赋值原理基本一致.

上一篇下一篇

猜你喜欢

热点阅读