为什么还要讲KVO的原理呢,如果想了解KVO原理,上网一查,可以搜出一大堆文章,不是更好?以前我也是上网一查,大概知道了怎么回事,但是呢,过一段时间,你肯定忘了,不知道你是否有同感,原因?
1. 你用零碎的时间学习零碎的知识,不能形成有意识的系统知识,这样只是一场过客,时间一长,你还得去重新查找,浪费时间和精力不说,你觉的有意思吗?
2. 网上的文章说的不全,比如说,说了对象,但是没有说容器;说了基本使用,没有说底层的实现等
3. 没有相关代码来佐证等
而本篇文章试图从最简单的用法说起,由浅入深,覆盖对象和容器,底层机制
一. 一般基本的使用,新建一个对象Animal,在头文件中加一个name的属性
图1同时在ViewControler中创建Animal的对象,同时监听该对象的name属性
图2结果:点击vc,得到结果如下:
图3这是一个对象完整的使用基本KVO的流程
说明:
NSKeyValueObservingOptionNew // 返回新值
NSKeyValueObservingOptionOld // 返回旧值
NSKeyValueObservingOptionInitial // 注册的时候就会发一次通知,改变的时候也会发通知
NSKeyValueObservingOptionPrior // 改变之前发一次,改变之后发一次
二. 上面是name改变值都会发送通知,但是现在我只要满足特定条件才发送通知,另外情况都不需要发送通知,怎么做呢?
iOS提供了2种模式给我们选择,一种是 默认的自动模式,一种是手动模式
2.1 手动模式
在Animal的m文件中重载实现automaticallyNotifiesOberversOfName函数,返回NO即可,结果是没有返回值
图4然后我想要只在touchesBegan函数中才改变值才通知,另外情况都不需要通知,如下
图5为什么需要willChange和didChange 2个函数呢?因为NSKeyValueObservingOptions的选项有返回旧值和新值,所以willChange对应返回旧值,didChange返回新值,也就是说这2个是成双成对出现的
三. 属性依赖,如果在Animal对象里有另外一个对象,然后要监听那个对象的属性呢,新增一个cat对象,增加age的属性,在Animal里增加一个Cat对象,同时在ViewController里监听Cat对象的age属性
图5另外一种情况是:如果在cat对象中有很多属性,这样在监听的时候是不是要写很多监听的代码呢,是否有简单的方法来统一监听所有的属性呢??
eg:
[self.a addObserver:self forKeyPath:@"cat.age" options:NSKeyValueObservingOptionNew context:nil];
[self.a addObserver:self forKeyPath:@"cat.kind" options:NSKeyValueObservingOptionNew context:nil];
....
iOS提供了一个方法来返回指定的属性容器,达到只要监听对象的目的
eg: 在ViewController中
[self.a addObserver:self forKeyPath:@"cat" options:NSKeyValueObservingOptionNew context:nil];
相应的在Animal的m文件中重载keyPathsForValuesAffectingValueForKey函数
四. 如果我要观察的是容器呢,比如NSMutableArray呢,我在Animal头文件中增加一个NSMutableArray的属性,在ViewController中进行addObject操作
图6 图7运行发现,像array里增加值,并不能发送通知,为什么??
结论:KVO监听的是属性的set方法,不是对象的变量,而addObject方法明显不是set方法,所以不会触发通知
如果NSMutableArray的addObject方法还不能证明的话,那么再通过一个例子来证明
在Animal头文件中声明一个name的变量
图8在ViewController中监听name属性
图9运行后发现,没有发送通知
五. 底层实现
5.1 当调用addObserver函数的时候,系统运行时自动帮你把Animal类替换为了 NSKVONotifying_Animal类,同时重写了set方法,当我们查看addObserver函数的时候,可以看到该函数在NSObject分类下@interfaceNSObject(NSKeyValueObserverRegistration)
这里大家要理清一个概念:方法跟类有关系,成员变量跟对象有关系
5.2 新增一个NSObject的一个分类来替换系统的分类
图10疑问:为什么要添加setName方法?自定义的子类没有该方法?是的,没有该方法,子类继承于父类,父类有,但是子类没有
六. 容器实现监听,使用KVC技术
iOS提供了mutableArrayValueForKey等多个KVC的方法来生成一个KVO的array,来实现addObject方法的监听
但是,我发现count不是数组的属性,不能进行监听修改,以上就是本文的内容,有不当之处欢迎指出。
七. 课后作业
7.1 不知道大家有没发现,对于change返回的kind值,对象和容器是不一样的,一个是1,一个是2,那么是否还有第三个,第四个... 呢,这个kind代表什么意思呢?
7.2. 关于在图10中的keyMethod方法,里面分别去调用父类的setName,同时调用observer函数,为什么不直接调用子类的setName方法??
关于这些问题,我会在下期的KVC详解中解答
网友评论
object_setClass(self, class_getSuperClass(myclass));
objc_msgSend(self, @selector(setName:), newName);
NSObject *observer = objc_getAllociatedObject(self, @"HS_observer");
这里isa不是已经修改为指向父类了吗, 父类怎么会有HS_observer属性呢,上面不是把HS_observer和子类关联的吗,