iOS底层原理 -- KVO的本质

使用方式

通过以下例子来总结使用方式

// ViewController.h
#import "ViewController.h"
#import "Person.h"

@interface ViewController ()
@property (nonatomic, strong) Person *person;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person = [[Person alloc] init];
    self.person = person;
    person.age = 10;
    person.age = 20;
    
    // 添加观察者为当前控制器,对age进行观察
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
    [person addObserver:self forKeyPath:@"age" options:options context:nil];
    
    person.age = 30;
}

- (void)dealloc {
    // 移除观察者,防止内存泄露
    [self.person removeObserver:self forKeyPath:@"age"];
}

// 当被观察的数据变化时,提醒观察者
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"改变的值---- %@", change);
    
}


@end

result: 
改变的值---- {
    kind = 1;
    new = 30;
    old = 20;
}

使用方式:
1、添加观察者

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

2、观察者实现对应的观察方法(数据变化时进行处理)

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)contex

3、移除观察者(防止内存泄露)

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

本质分析

原来上面的例子Person中person.age = 10、person.age = 20与person.age = 30在写法上没有任何区别,都是调用了setter方法。而唯有添加了观察者后的,person.age = 30才有反应。
从编译时看不出来变化,说明在KVP是在runtime时动了手脚。
在添加观察者前后分别打断点,可以看到isa指针变化了。

添加观察者前.png
添加观察后.png

实际上加了KVO后,对象的isa指针会重新指向 “NSKVONotifying_对象” (实际上市该类对象的子类)
而在 “NSKVONotifying_对象”该类对象中 重新了setter方法。
重写方法中实际调用过程为

[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];

而在didChangeValueForKey:方法中,会调用观察者的观察方法即[obser observeValueForKeyPath:@"age" ofObject:self change:{} content:nil];

为了证明这个过程,操作如下
Person.m文件中以下方法

// Person.m
- (void)willChangeValueForKey:(NSString *)key {
    NSLog(@"%s", __func__);
    [super willChangeValueForKey:key];
}

- (void)setAge:(int)age {
    NSLog(@"%s", __func__);
    _age = age;
}

- (void)didChangeValueForKey:(NSString *)key {
    NSLog(@"%s",  __func__);
    [super didChangeValueForKey:key];
    NSLog(@"%s", __func__);
}

ViewController.m文件中以下方法

//  ViewController.m
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"%s", __func__);    
}

打印结果如下

-[Person setAge:]
-[Person setAge:]
// KVO 的过程
-[Person willChangeValueForKey:]
-[Person setAge:]
-[Person didChangeValueForKey:]
-[ViewController observeValueForKeyPath:ofObject:change:context:]
-[Person didChangeValueForKey:]

上述表明KVO调用过程实际就是

[self willChangeValueForKey:@"观察的值"];
[super setAge:值];
[self didChangeValueForKey:@"观察的值"];

而在didChangeValueForKey:中又调用了观察者的观察方法即observeValueForKeyPath:ofObject:change:context方法。

问题

1、KVO本质是什么?
本质是改变了对象isa指针的指向并重写了setter方法,指向一个NSKVONotifying_对象的子类对象。
重写setter方法,如下
1)willChangeValueForKey:
2)父类原来的setter
3)didChangeValueForKey:
内部会触发观察者(Oberser)的观察方法( observeValueForKeyPath:ofObject:change:context:)

2、如何手动触发KVO?
手动调用调用willChangeValueForKey:和didChangeValueForKey:方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • 问题 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?) 如何手动触发KVO ? 首先需要了解KVO...
    hjltony阅读 574评论 0 2
  • 面试问题: · iOS用什么方式实现对一个对象的KVO? · 如何手动触发KVO? 我们通过以下几个点来寻找这两个...
    高思阳阅读 238评论 0 1
  • KVC KVC定义 KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过K...
    暮年古稀ZC阅读 2,120评论 2 9
  • 对小码哥底层班视频学习的总结与记录。面试题部分,通过对面试题的分析探索问题的本质内容。 问题iOS用什么方式实现对...
    xx_cc阅读 10,739评论 26 64
  • 一、KVO 的使用 KVO 的全称 Key-Value Observing,俗称“键值监听”,可以用于监听某个对象...
    666真666阅读 658评论 0 1