刨根问底KVO

KVO 全称 Key-Value Observing。中文叫键值观察。KVO其实是一种观察者模式,观察者在键值改变时会得到通知,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身。相比Notification,KVO更加的简单直接。

KVO的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVO操作。

KVO的使用也很简单,就是简单的3步。

  1. 注册需要观察的对象的属性addObserver:forKeyPath:options:context:
  2. 实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用.在这个方法中还通过NSKeyValueObservingOptionNew这个参数要求把新值在dictionary中传递过来。
  3. 取消注册观察removeObserver:forKeyPath:context:

我们观察下代码实现,探究实现原理:

Person.h

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>

@interface Person : NSObject

- (void)registerObserver;

@property (nonatomic, assign) NSInteger age;

@end

Person.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#import "Person.h"

@implementation Person

- (void)registerObserver {

    [self addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
} - (NSString *)description {
return [NSString stringWithFormat:@"%@,%ld",[self valueForKey:@"isa"],self.age]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"age"]) {
NSLog(@"%@",change);
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
} - (void)dealloc {
[self removeObserver:self forKeyPath:@"age"];
}

main函数

1
2
3
4
5
6
7
8
Person * p = [[Person alloc] init];
p.age = 20;
NSLog(@"%@",p); [p registerObserver]; p.age = 30;
NSLog(@"%@",p);

运行程序打印出的log日志为:

1
2
3
4
5
6
7
Person,20
{
kind = 1;
new = 30;
old = 20;
}
NSKVONotifying_Person,30

不要太惊讶,我们慢慢解释。

我重写的Person类的description方法,使用KVC拿到isa指针,获取到self的类名,我们发现在使用KVO之前类名是Person,但注册了KVO之后,类名变成了NSKVONotifying_Person

主要是因为KVO的实现使用了isa-swizzling。在程序运行时Person会生成一个派生类NSKVONotifying_Person,在这个派生类中重写基类中任何被观察属性的setter方法,用来欺骗系统顶替原先的类。在setter方法中实现真正的通知机制.

1
2
3
4
5
6
7
//可以到伪代码

- (void)setAge:(int)age
{
[super setAge:age];
[监听器 observeValueForKeyPath:@"age" ofObject:self change:@{} context:nil];
}

我们又可以猜测,使用KVO,内部一定执行setter方法。

当我们把上面代码 p.age = 30;改成p->_age = 30;

你会发现KVO的方法不走了,也证实了这点。

如果p不提供setAge,getAge方法,还想用KVO.

1
2
3
[p willChangeValueForKey:@"age"];
p->_age = 30;
[p didChangeValueForKey:@"age"];

苹果官方KVO文档:

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html

另外.....

我的愿望是.......

世界和平.........

iOS设计模式 —— KV0的更多相关文章

  1. IOS设计模式之一(MVC模式,单例模式)

    iOS 设计模式-你可能已经听说过这个词,但是你真正理解它意味着什么吗?虽然大多数的开发者可能都会认为设计模式是非常重要的,然而关于设计模式这一主题的文章却不多,并且有时候我们开发者在写代码的时候也不 ...

  2. iOS 设计模式之工厂模式

    iOS 设计模式之工厂模式 分类: 设计模式2014-02-10 18:05 11020人阅读 评论(2) 收藏 举报 ios设计模式 工厂模式我的理解是:他就是为了创建对象的 创建对象的时候,我们一 ...

  3. iOS设计模式之生成器

    iOS设计模式之生成器 1.生成器模式的定义 (1): 将一个复杂的对象的构件与它的表示分离,使得相同的构建过程能够创建不同的表示 (2): 生成器模式除了客户之外还包括一个Director(指导者) ...

  4. IOS设计模式之三:MVC模式

    IOS设计模式之三:MVC模式   模型-视图-控制器 这个模式其实应该叫做MCV,用控制器把model与view隔开才对,也就是model与view互相不知道对方的存在,没有任何瓜葛,他们就像一个团 ...

  5. iOS设计模式 - 享元

    iOS设计模式 - 享元 原理图 说明 享元模式使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件.通常物件中的部分 ...

  6. iOS设计模式 - 责任链

    iOS设计模式 - 责任链 原理图 说明 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...

  7. iOS设计模式 - 模板

    iOS设计模式 - 模板 原理图 说明 定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. 源码 https://github.c ...

  8. iOS设计模式 - 访问者

    iOS设计模式 - 访问者 原理图 说明 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 1.Visitor 抽象访问者角色,为该对象结构中具 ...

  9. iOS设计模式 - 迭代器

    iOS设计模式 - 迭代器 原理图 说明 提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示. 源码 https://github.com/YouXianMing/iOS-Des ...

随机推荐

  1. NO9——线段相关

    #include <stdio.h> #include <iostream> #include <math.h> #include <algorithm> ...

  2. ajax中用jsonp接收json数据

    最近在做查快递网页时遇到一个问题,调用的快递100的api,但是快递100api不允许跨域请求,就是用127.0.0.1发的请求会直接被拦截. 只是个简单的网页,不想自己做服务器转发,最后找到了一个y ...

  3. C语言分支/顺序作业总结

    总结 1.1 基本要求(1分) 按时交 - 有分 未交 - 0分 迟交一周以上 - 倒扣本次作业分数 抄袭 - 0分 博客作业格式不规范,没有用Markdown语法 -扣分 泛泛而谈(最多七分) 1. ...

  4. java课程设计——2048

    1.团队介绍 2.项目git地址 [https://gitee.com/SMTwula/java036.git] 3.项目git提交记录截图 沈梦婷 田亚琴 4.项目功能架构图与主要功能流程图 5.项 ...

  5. 使用 window.getSelection() 方法获取鼠标划取部分的起始位置和结束位置的问题(高亮后不能正确获取)

    如果没有高亮等复杂处理,只需要获取一段文字中选取的字和位置,那么 使用window.getSelection()获取div中选中文字内容及位置 怎么获取textarea中选中文字 则可以满足需求: - ...

  6. Hibernate对象状态

    对象状态 瞬时(transient) 自己new出来的对象,数据库没有记录与之对应,与session也没有关联 持久(persistent) 数据库中有记录与之对应,当前与session有关联,相关的 ...

  7. 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

  8. Luogu3959 NOIP2017宝藏(状压dp)

    按层dp,f[i][j]表示已扩展i子集的节点当前在第j层的最小代价,预处理点集间距离即可. #include<iostream> #include<cstdio> #incl ...

  9. TCP/IP Note3

    TCP/IP协议 TCP/IP是不同的通信协议的大集合. 协议族:TCP/IP是基于TCP和IP这两个最初的协议之上的不同的通信协议的大集合. 1. TCP - 传输控制协议 TCP用于从应用程序到网 ...

  10. 获取Parameter参数值,方便调试使用

    #region #warning 调试使用,获取sql参数化,拼接出完整的sql语句,复制sql明文到mssql中运行 string debugSql = queryHelper.CommandTex ...