KVO中你所不知道的"坑"

一、什么是 KVO

  首先让我们了解一下什么KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应。键值观察Key-Value-Observer就是观察者模式。

  观察者模式的定义:一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象。这个主动通知通常是通过调用各观察者对象所提供的接口方法来实现的。观察者模式较完美地将目标对象与观察者对象解耦。

  KVO和KVC没有什么关系,要说有关系的话也就是--KVO同KVC一样都依赖于Runtime的动态机制.

  在WPF中有一种双向绑定机制,如果数据模型修改了之后会立即反映到UI视图上,类似的还有如今比较流行的基于MVVM设计模式的前端框架。其实在ObjC中原生就支持这种机制,它叫做Key Value Observing(简称KVO)。KVO其实是一种观察者模式,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身。在ObjC中要实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用KVO。

二、怎么实现 KVO

  • 注册
1 //keyPath就是要观察的属性值
2 //options给你观察键值变化的选择
3 //context方便传输你需要的数据
4  -(void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
5  options:(NSKeyValueObservingOptions)options context:(void *)context;
  • 实现方法
1 //change里存储了一些变化的数据,比如变化前的数据,变化后的数据;如果注册时context不为空,这里context就能接收到。
2  -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
3
4  change:(NSDictionary *)change context:(void *)context
  • 移除
1  //移除
2  - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

三、 KVO底层实现分析

系统实现KVO有以下几个步骤:

  当类A的对象第一次被观察的时候,系统会在运行期动态创建类A的派生类。我们称为B。

  在派生类B中重写类A的setter方法,B类在被重写的setter方法中实现通知机制。

  类B重写会 class方法,将自己伪装成类A。类B还会重写dealloc方法释放资源。

  系统将所有指向类A对象的isa指针指向类B的对象。

通俗一点的解释是:当注册观察者的时候做的哪些事情:

  1.动态的创建一个叫NSKVONotifying_Person的子类

   2.更改之前类的 isa指针为子类

   3.传入一堆参数 1.监听者(将来调用observeValueForKeyPath)  2.keypath(决定了重写哪个set方法)  3.枚举(决定传哪些给你) 4.携带参数

   4.根据keypath 重写子类的set方法

1     //其实在子类的set方法中是实现了下面三步
2     [super setWeight:weight];
3
4     //这两个方法会调用监听者的监听者方法
5     [self willChangeValueForKey:@"weight"];
6     [self didChangeValueForKey:@"weight"];

   5.在子类的set方法中  根据枚举 保存所有的属性值  然后调用父类的set方法 然后调用监听者的observeValueForKeyPath... 把对应的值传出去通知监听者发生了事情。所以不能依靠isa指针来确定对象是否是一个类的成员。应该使用class方法来确定对象实例的类。

四、KVO使用陷阱介绍:

  首先,看一下KVO的使用场景,假设我们的目标是在一个UITableViewController内对tableview的contentOffset进行实时监测,很容易地使用KVO来实现为[使用场景]。

  在初始化方法中加入:

1 [_tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];

  在dealloc中移除KVO监听:

1  [_tableView removeObserver:self forKeyPath:@"contentOffset" context:nil];

  添加默认的响应回调方法:

1 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
3  change:(NSDictionary *)change context:(void *)context
5  {
7  [self doSomethingWhenContentOffsetChanges];
9  }

  通常的写法已经完成,但是当你在controller中添加多个KVO时,所有的回调都是走同上述函数,那就必须对触发回调函数的来源进行判断。判断如下:

 1  - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
 2  change:(NSDictionary *)change context:(void *)context
 3  {
 4  if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
 5  [self doSomethingWhenContentOffsetChanges];
 6   }
 7  }

  接着还有其他的陷阱,如 我们假设当前类(在例子中为UITableViewController)还有父类,并且父类也有自己绑定了一些其他KVO. 我们看到,上述回调函数体中只有一个判断,如果这个if不成立,这次KVO事件的触发就会到此中断了。但事实上,若当前类无法捕捉到这个KVO,那很有可能是在他的superClass,或者super-superClass...中,上述处理截断了这个链。合理的处理方式应该是这样的:

1  - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
2  change:(NSDictionary *)change context:(void *)context
3  {
4  if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
5  [self doSomethingWhenContentOffsetChanges];
6  } else {
7  [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
8  }
9  }

  还有潜在的问题有可能出现在dealloc中对KVO的注销上。KVO的一种缺陷(其实不能称为缺陷,应该称为特性)是,当对同一个keypath进行两次removeObserver时会导致程序crash,这种情况常常出现在父类有一个kvo,父类在dealloc中remove了一次,子类又remove了一次的情况下。不要以为这种情况很少出现!当你封装framework开源给别人用或者多人协作开发时是有可能出现的,而且这种crash很难发现。不知道你发现没,目前的代码中context字段都是nil,那能否利用该字段来标识出到底kvo是superClass注册的,还是self注册的?

  回答是可以的。我们可以分别在父类以及本类中定义各自的context字符串,比如在本类中定义context为@"ThisIsMyKVOContextNotSuper";然后在dealloc中remove observer时指定移除的自身添加的observer。这样iOS就能知道移除的是自己的kvo,而不是父类中的kvo,避免二次remove造成crash。

时间: 2024-10-29 05:19:12

KVO中你所不知道的"坑"的相关文章

JavaScript中你所不知道的Object(二)--Function篇

上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.String.Function等有更多的内部属性,而它们分别是什么呢? 这些内部属性不能像Object的内部属性一样一言以蔽之,因为它们各有各的用处和特点.其中核心的部分自然是最特殊的对象,Function对象.我们先从简单的开始: [[PrimitiveValue]]: 值的类型是基础数据类型.所以所有

KVO你所不知道的"坑"

一.什么是 KVO 首先让我们了解一下什么KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应.键值观察Key-Value-Observer就是观察者模式. 观察者模式的定义:一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象.这个主动通知通常是通过调用各观察者对象所提供的接口方法来实现的.观察者模式较完美地将目标对象与观察者对象解耦. KVO和KVC没有什么关系,要说有关系的话也就是--K

JAVA重载(overload)和覆盖(override)中你所不知道的陷阱

大家都知道重载是指在同一个类中,定义了有相同名称但是有不同参数类型的方法时,到底调用那一个方法会根据参数类型来选择.我们来看下面这个例子: public class ParentClass { } public class ChildClass extends ParentClass{ } public class Test { public void testOverLoad(ParentClass cls){ System.out.println("It's ParentClass"

java中你所不知道的CAS操作

1.CAS是什么 Compare and Swap(比较并操作),由处理器架构支持,语义是如果当前值V和旧值A相同,则将当前值修改为B,如果不相同则不修改.CAS操作采用的是乐观锁技术,当多线程同时修改某个变量时只有一个成功,其他线程会失败当是不会被挂起,会被告知失败并重试.2.CAS操作和synchronized有什么区别呢 synchronized关键字采用悲观锁技术,线程独享锁,其他线程会被挂起知道锁被释放线程恢复,挂起和恢复会有很大的开销.3.java中CAS操作有哪些 java1.5之

Go基础之--位操作中你所不知道的用法

之前一直忽略的就是所有语言中关于位操作,觉得用处并不多,可能用到也非常简单的用法,但是其实一直忽略的是它们的用处还是非常大的,下面先回顾一下位操作符的基础 位操作符 与操作:&1 & 1 = 11 & 0 = 00 & 1 = 00 & 0 = 0 或操作:!1 | 1 = 11 | 0 = 10 | 1 = 10 & 0 = 0 异或:^1 ^ 1 = 01 ^ 0 = 10 ^ 1 = 10 ^ 0 = 0 左移:<<1 << 1

浅谈在网页中你所不知道的css背景属性

在很多网页设计中,很多人对于css的背景属性,只是停留在设置背景.今天我们来谈谈它的其他应用. 比如背景的定位,它能实现很多翻转网页效果. background-position:指定背景图像的位置background-size 指定背景图片的大小background-image 指定要使用的一个或多个背景图像background-repeat 指定如何重背景图像background-origin 指定背景图像的定位区域background-clip 指定背景图像的绘画区域background-

你所不知道的html5与html中的那些事(二)

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5的父亲html的一些

你所不知道的html5与html中的那些事(一)

分类: Web开发 文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5

你所不知道的html5与html中的那些事(四)——文本标签

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5的父亲html的一些