KVO的基本原理大概是这样的

  当一个对象被观察时, 系统会新建一个子类NSNotifying_A ,在子类中重写了对象被观察属性的 set方法,  并且改变了该对象的 isa 指针的指向(指向了新建的子类) , 当属性的值发生改变了, 会调用子类的set方法, 然后发出通知

一. KVO 的基本使用

给_person对象 添加观察者self, 当person对象的name的值发生改变的时候, 会触发observer方法

_person = [Person new]; p.name = @"oldName"; 
//添加观察者
// [p addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];
[_person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
// 所观察的对象的keyPath 发生改变的时候, 会触发
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",keyPath);
NSLog(@"%@",change); }

二.  当keyPath 为对象时, 改对象有许多属性, 怎么办?

在person类中,重写这个方法, 设置需要观察的属性 , 注意:"_dog.level"

//返回一个容器, 里面放的是NSString类型
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
//观察dog对象中的所有属性
if ([key isEqualToString:@"dog"]) {
keyPaths = [keyPaths setByAddingObjectsFromArray:@[@"_dog.level",@"_dog.age"]];
} return keyPaths;
}

三. 手动触发KVO

系统默认该对象的所有属性 都能被观察到 ,重写下面方法, 可以单独设置某个属性不能被观察

//默认 yes, 默认自动观察所有属性
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
return YES;
}
//返回NO, 则不能被默认观察到name
+ (BOOL)automaticallyNotifiesObserversOfName{
return NO;
}
+ (BOOL)automaticallyNotifiesObserversOfAge{
return YES;
}

当 name 发生改变的时, 手动触发, 发送通知

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//手动发通知
//即将改变(发一次通知)
[_person willChangeValueForKey:@"name"];
_person.name = @"dddd";
//已经改变(发一次通知),一共发了两次通知
[_person didChangeValueForKey:@"name"];
}

四. 自定义KVO

根据kvo的原理, 可以自定义一个kvo, 建一个NSObject的分类, 添加方法

@interface NSObject (XSKVO)

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

@end

通过runtime的方式, 动态创建一个类, 并给该类添加方法

#import "NSObject+XSKVO.h"
#import <objc/runtime.h> @implementation NSObject (XSKVO) - (void)xs_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{ //1.新建一个类
NSString *className = [@"XSKVO" stringByAppendingString: NSStringFromClass([self class])];
Class newClass = objc_allocateClassPair([self class], className.UTF8String, );
//注册类
objc_registerClassPair(newClass);
//2.修改 调用者类型
object_setClass(self, newClass); //3.给子类添加set方法(子类里面没有set方法的)
//OC方法:方法编号SEL ,方法实现IMP
class_addMethod(newClass, @selector(setName:), xssetName, ""); } /*
隐藏的参数:
self 方法的调用者
_cmd 方法的编号 */
void xssetName(id self,SEL _cmd, NSString *newName){
NSLog(@"自定义的实现%@",newName);
//方案一:通过消息机制 发送消息 -observeValueForKeyPath } @end

五. 其他

关于容器类(如:NSMutableArray)的观察, 当通过addObject: 向数组中添加对象, 不会触发KVO, 因为并没有触发set方法,

解决方法: 通过KVC 方法 - mutableArrayValueForKey:

KVO 使用及原理的更多相关文章

  1. KVO内部实现原理

    KVO的原理: 只要给一个对象注册一个监听, 那么在运行时, 系统就会自动给该对象生成一个子类对象, (格式如:NSKVONotifying_className), 并且重写自动生成的子类对象的被监听 ...

  2. KVO的实现原理探寻

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  3. KVO底层实现原理,仿写KVO

    这篇文章简单介绍苹果的KVO底层是怎么实现的,自己仿照KVO的底层实现,写一个自己的KVO监听 #pragma mark--KVO底层实现 第一步:新建一个Person类继承NSObject Pers ...

  4. KVC与KVO的实现原理

    |KVC的用法 1.KVC既键值编码(Key Value Coding),基于NSKeyValueCoding协议,它是以字符串的形式来操作对象的成员变量,也就是通过字符串key来指定要操作的成员变量 ...

  5. IOS KVO的实现原理

    #import "HMViewController.h" #import "HMPerson.h" @interface HMViewController () ...

  6. KVC/KVO原理详解及编程指南

    一.简介 1.KVC简介 2.KVO简介 二.KVC相关技术 1.Key和Key Path 2.点语法和KVC 3.一对多关系(To-Many)中的集合访问器方法 4.键值验证(Key-Value V ...

  7. 【转】 KVC/KVO原理详解及编程指南

    原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 前言: 1.本文基本不讲KVC/KVO的用法,只结合网上的资料说说对这种技术的 ...

  8. 转:KVC/KVO原理详解及编程指南

      作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 转载请注明出处 如果觉得文章对你有所帮助,请通过留言或 ...

  9. KVO原理解析

    KVO在我们项目开发中,经常被用到,但很少会被人关注,但如果面试一些大公司,针对KVO的面试题可能如下: 知道KVO嘛,底层是怎么实现的? 如何动态的生成一个类? 今天我们围绕上面几个问题,我们先看K ...

随机推荐

  1. Halcon中一些突然想不起来但确实有用的算子

    1.Develop dev_display  在现有图形窗口中显示图像目标. dev_set_color   设置一个或更多输出颜色,通常用于设置region或者xld的颜色. dev_set_dra ...

  2. 最近公共祖先 LCA Tarjan算法

    来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个 ...

  3. Spring.net init-method destroy-method

    <object id="exampleInitObject" type="Examples.ExampleObject" init-method=&quo ...

  4. nhibernate 3.x新特性

    http://www.360doc.com/content/14/0226/17/9316347_355904008.shtml

  5. rocketmq配置项说明(对应版本:4.0.0-incubating)

    Broker配置参数说明 自定义客户端行为 ※一些默认配置的源代码路径 org.apache.rocketmq.store.config --END--

  6. 工作流系统webservice服务

    http://blog.csdn.net/zhengzhb/article/details/7477616

  7. CodeForces 342C Cupboard and Balloons (几何问题)

    题意:给定一个 r 和 h,r 是上面那个半球的半径,h 是下面那个圆柱的高度,然后有一些半径为 r/2的气球,问你最多能放几个. 析:根据题意,很容易知道,先从下面往上放,两个两个的放,放到不能放的 ...

  8. static 和final

    1.static       static关键字可以用来修饰类的变量,方法和内部类.static是静态的意思,也是全局的意思,它定义的东西属于全局,与类相关,不与具体实例相关.就是说它调用的时候,只是 ...

  9. 常用的String方法Math方法

    Arrays.sort();冒泡排序字符串.charAt(i);字符串索引i上的字符Integer.prsent(字符串) 字符串转整数equals(Object anObject) 将此字符串与指定 ...

  10. Windows装python

    pycharm常用快捷键ctr+alt+shift+l可以快速格式化python安装下载地址https://www.python.org/downloads/release/python-365/ 一 ...