KVO 使用及原理
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 使用及原理的更多相关文章
- KVO内部实现原理
KVO的原理: 只要给一个对象注册一个监听, 那么在运行时, 系统就会自动给该对象生成一个子类对象, (格式如:NSKVONotifying_className), 并且重写自动生成的子类对象的被监听 ...
- KVO的实现原理探寻
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- KVO底层实现原理,仿写KVO
这篇文章简单介绍苹果的KVO底层是怎么实现的,自己仿照KVO的底层实现,写一个自己的KVO监听 #pragma mark--KVO底层实现 第一步:新建一个Person类继承NSObject Pers ...
- KVC与KVO的实现原理
|KVC的用法 1.KVC既键值编码(Key Value Coding),基于NSKeyValueCoding协议,它是以字符串的形式来操作对象的成员变量,也就是通过字符串key来指定要操作的成员变量 ...
- IOS KVO的实现原理
#import "HMViewController.h" #import "HMPerson.h" @interface HMViewController () ...
- KVC/KVO原理详解及编程指南
一.简介 1.KVC简介 2.KVO简介 二.KVC相关技术 1.Key和Key Path 2.点语法和KVC 3.一对多关系(To-Many)中的集合访问器方法 4.键值验证(Key-Value V ...
- 【转】 KVC/KVO原理详解及编程指南
原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 前言: 1.本文基本不讲KVC/KVO的用法,只结合网上的资料说说对这种技术的 ...
- 转:KVC/KVO原理详解及编程指南
作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 转载请注明出处 如果觉得文章对你有所帮助,请通过留言或 ...
- KVO原理解析
KVO在我们项目开发中,经常被用到,但很少会被人关注,但如果面试一些大公司,针对KVO的面试题可能如下: 知道KVO嘛,底层是怎么实现的? 如何动态的生成一个类? 今天我们围绕上面几个问题,我们先看K ...
随机推荐
- sqlserver全备份,差异备份和日志备份
差异备份是以上一个全备为基点,这个期间所有差异数据的备份. 日志备份是基于前一个全备+日志备份为基点,这个期间的事务日志的备份.(日志备份用于确保还原数据库到某个时间点) 在利用全备+日志备份 ...
- 1097G Vladislav and a Great Legend
传送门 分析 https://blog.csdn.net/forever_shi/article/details/88048528 代码 #include<iostream> #inclu ...
- 使用Selenium&PhantomJS的方式爬取代理
前面已经爬取了代理,今天我们使用Selenium&PhantomJS的方式爬取快代理 :快代理 - 高速http代理ip每天更新. 首先分析一下快代理,如下 使用谷歌浏览器,检查,发现每个代理 ...
- centos7如何知道jdk的在哪个目录
今天一个小实验需要安装jdk,用命令Java -version查询了一下,原来Centos7自带OpenJDK的环境,但是需要手动配置/etc/profile文件,于是开始找java的安装路径.... ...
- 3.1.2 condition 条件
package 第三章.重入锁; import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Reent ...
- k8s启动Pod遇到CrashLoopBackOff的解决方法
1.用kubectl get pod 当看到上面的状态后执行第2步 2.查看pod详情 [root@cc hzb]# kubectl describe pod ceph-mysql-hzb-pod 找 ...
- linux 安装php7
http://blog.csdn.net/whatday/article/details/50645117 1: wget http://cn2.php.NET/distributions/php- ...
- Google Tango Java实例程序
Java API:https://developers.google.com/tango/apis/java/reference/ 1. java_augmented_reality_example ...
- 编写高质量代码改善C#程序的157个建议——建议145:避免过长的方法和过长的类
建议145:避免过长的方法和过长的类 如果违反“一个方法只做一件事”及类型的“单一职责原则”,往往会产生过长的方法和过长的类. 如果方法过长,意味着可以站在更高的层次上重构出若干更小的方法.以行数作为 ...
- ERROR: from PIL import Image ImportError: No module named PIL
ERROR: from PIL import Image ImportError: No module named PIL 到 http://www.pythonware.com/products/p ...