KVO分析

简介: KVO 概述 KVO的全称是NSKeyValueObserving,对象采用的一种非正式协议,当其他对象的指定属性发生变化时,通知对象。由于KVO的机制,只对对象的属性起作用,一般继承自NSObject都支持KVO。

KVO

概述

KVO的全称是NSKeyValueObserving,对象采用的一种非正式协议,当其他对象的指定属性发生变化时,通知对象。由于KVO的机制,只对对象的属性起作用,一般继承自NSObject都支持KVO。

您可以观察任何对象属性,包括简单属性、一对一关系和多对多关系。

使用

KVO使用通常需要三个步骤

  1. 通过 addObserver:forKeyPath:options:context:注册观察者,来观察keyPath的变化
  2. 当相对于被观察对象的指定键路径的值发生变化时,通知观察对象的observeValueForKeyPath:ofObject:change:context:这个方法,观察者应当实现该方法
  3. 当不再需要观察对象属性变化时,通过调用removeObserver:forKeyPath:或者removeObserver:forKeyPath:context:阻止观察者对象接收与接收此消息的对象相关的键路径指定的属性的更改通知.

注:addObserverremoveObserver应当一一对应

注册方法

在注册时,options的值为NSKeyValueObservingOptions枚举类型
。当为NSKeyValueObservingOptionNewNSKeyValueObservingOptionOld时,表示接收新值和旧值,默认为只接收新值。如果想在注册观察者后,立即接收一次回调,则可以加入NSKeyValueObservingOptionInitial枚举

实现原理

KVO是通过isa-swizzling技术实现的。在运行时根据原类创建一个中间类,这个中间类是原类的子类,并动态修改当前对象的isa指向中间类。并且将class方法重写,返回原类的Class。所以苹果建议在开发中不应该依赖isa指针,而是通过class实例方法来获取对象类型。

验证

` _stu = [Student new];
  _stu.stu_id = index;
  NSLog(@"beforeClass:%@",  object_getClass(_stu));
 [_stu addObserver:self forKeyPath:@"age"    options:NSKeyValueObservingOptionNew context:nil];
 NSLog(@"afterClass:%@", object_getClass(_stu));`

可以查看输出结果为:

`2018-12-30 14:34:01.761066+0800 KVO[1485:239369]           beforeClass:Student`

2018-12-30 14:34:01.761533+0800 KVO[1485:239369] afterClass:NSKVONotifying_Student

自定义KVO

写一个Student的分类

Student+KVO.h

 `-(void)XK_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;`

Student+KVO.m

#import "NSObject+KVO.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation Student  (KVO)
-(void)XK_addObserver:(NSObject *)observer  forKeyPath:(NSString *)keyPath options: (NSKeyValueObservingOptions)options context:(void *)context{
NSString* oldClassName = NSStringFromClass([self class]);
NSString* newClassName = [@"XKKVONotyfing_" stringByAppendingString:oldClassName];
const char * newName = [newClassName UTF8String];
Class newClass = objc_allocateClassPair([self class], newName, 0);
class_addMethod(newClass,@selector(setAge:), (IMP)setAge, "v@:i");
objc_registerClassPair(newClass);
object_setClass(self, newClass);
objc_setAssociatedObject(self,          
(__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}

void setAge(id self, SEL _cmd, int age) {
Class myClass = [self class];
object_setClass(self, class_getSuperclass([self class]));
//调用父类
id (*msgSend) (id, SEL,...) = (void *)objc_msgSend;
msgSend(self, @selector(setAge:),age);
id observer = objc_getAssociatedObject(self, (__bridge const void *)@"objc");
 id (*msgSendObj) (id, SEL,id,...) = (void *)objc_msgSend;
   msgSendObj(observer,@selector(XK_observeValueForKeyPath:ofObject:change:context:),@"age",@"age",nil,nil);
//改为子类
object_setClass(self, myClass);
}

观察者中调用

<[_stu XK_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

- (void)XK_observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary)change context:(void *)context{
 NSLog(@"keyPath:%@",keyPath);
 NSLog(@"ofObject:%@",object);
 NSLog(@"change:%@",change);
 NSLog(@"afterClass:%@", object_getClass(_stu));
}</code></pre>
目录
相关文章
|
前端开发 Java API
对象状态分析|学习笔记
快速学习对象状态分析
|
Android开发 iOS开发
iOS开发:KVC与KVO
KVC 就是键值编码(key-value-coding),可以直接访问对象的属性,或者给对象的属性赋值。黑魔法之一,很多高级的iOS开发技巧都是基于KVC实现的。 KVO 是键值观察者(key-value-observing)。实现方式:通过对某个对象的某个属性添加观察者,当该属性改变,就会调用”observeValueForKeyPath:”方法,为我们提供一个“对象值改变了!”的时机进行一些操作。
245 0
iOS开发:KVC与KVO
|
Android开发
事件分发四部曲之二《嵌套滑动事件分析》
事件分发四部曲之二《嵌套滑动事件分析》
事件分发四部曲之二《嵌套滑动事件分析》
|
测试技术
performSelector的原理及应用场景分析
performSelector的原理及应用场景分析
146 0
|
架构师 开发者 iOS开发
探究ReactiveCocoa 底层KVO封装流程
一、对比原生KVO,初识ReactiveCocoa的KVO * 我们先来看一段代码,通过触屏来动态修改视图背景色 @interface ViewController () @property (nonatomic, strong)UIColor * bgColor; @end @implemen...
3120 0
|
存储 自然语言处理 安全
KVO 解析
KVO解析(一) —— 基本了解KVO解析(二) —— 一个简单的KVO实现KVO解析(三) —— KVO合规性KVO解析(四) —— Faults and KVO Notifications
842 0