IOS底层

iOS 底层解析-----KVO与KVC原理

2019-02-28  本文已影响88人  Mr丨Yang

上文链接从isa指针看继承关系

KVO 全称Key-Value Observing,俗称"键值监听",可以用于监听某个对象属性值的改变

KVO 基本使用方法

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[MJPerson alloc] init];
    self.person1.age = 1;
    self.person1.height = 11;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 2;
    self.person2.height = 22;
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    [self.person1 addObserver:self forKeyPath:@"height" options:options context:@"456"];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.person1.age = 20;
    self.person2.age = 20;
    
    self.person1.height = 30;
    self.person2.height = 30;
}

// 当监听对象的属性值发生改变时,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}

- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
    [self.person1 removeObserver:self forKeyPath:@"height"];
}

从上述代码可看出,alloc出来的person1 与 person2 实例对象,在点击屏幕的时候,只监测到了person1对象值的变化,没有监测到person2对象的变化?从代码的角度考虑是因为person2对象没有添加KVO,那么添加了KVO之后,person1实例对象在内存中发生了什么变换?

先看person2实例对象的内存调用图


未使用KVO监听.png

person1实例对象使用了KVO 那么它的内存调用图如下

使用KVO监听.png

OC在运行时的时候自动生成了一个NSKVONotifying_MJPerson 类对象作为MJPerson类对象的子类对象,通过底层C _NSSetIntValueAndNotify方法 重写了setAge:方法,进而实现KVO的过程

以下可理解为实现该过程的伪代码

#import "MJPerson.h"

@interface NSKVONotifying_MJPerson : MJPerson

@end


#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];
}

@end

总结如图所示


调用过程总结.png

从上图可以看出来如果添加了KVO又不想修改属性值,那怎么主动调用监听方法呢,即可以调用以下两个方法主动去触发监听方法

[self willChangeValueForKey:@"age"];

[self didChangeValueForKey:@"age"];

KVC 全称Key-Value Coding,俗称"键值编码",可以通过一个Key来访问某个属性

KVC常用方法 (经常用到,用法不做过多解释)

//赋值
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath; 
-(void)setValue:(id)value forKey:(NSString *)key;

//取值
-(id)valueForKeyPath:(NSSting *)keyPath;
-(id)valueForKey:(NSSting *)key;

直接展示原理图

赋值逻辑

赋值.png

取值逻辑

取值.png

注意:赋值,或 取值 都是有查找顺序的

问题:KVC 修改属性会触发KVO么?
会触发KVO的
因为KVC内部会实现willChangeValueForKey: didChangeValueForKey: 方法 具体笔记不再论证

下篇文章链接

上一篇下一篇

猜你喜欢

热点阅读