书读百变,其义自见!

将KVO形式以代码实现呈现,通俗易懂,更容易掌握 :GitHub   -链接如果失效请自动搜索:https://github.com/henusjj/KVO_base

代码中有详细的注释

一、KVO-常用方法

//注册
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;//监听方法
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;//移除
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;//监听模式(手动,自动),默认是自动Yes
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;//属性的依赖,返回监听属性类的集合+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key;

二、KVO-基本使用

KVO监听属性值变化,从而做业务逻辑处理,监听属性变化,我们需要实现三步走

1.注册监听对象

2.实现监听方法

3.移除监听对象,避免crash

//
//  ViewController.m
//  KVO-基本用法
//
//  Created by GuoYanjun on 2019/1/9.
//  Copyright © 2019年 shiyujin. All rights reserved.
//

#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property(nonatomic,strong)Person *p;
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];_p=[[Person alloc]init];
//    注册
    [_p addObserver:self forKeyPath:NSStringFromSelector(@selector(name)) options:NSKeyValueObservingOptionNew context:nil];}//监听方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{NSLog(@"%@",change);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{//    自动模式static int a =0;_p.name = [NSString stringWithFormat:@"%d",a++];//    手动
//    [_p willChangeValueForKey:@"name"];
//    _p.name = [NSString stringWithFormat:@"%d",a++];
//    [_p didChangeValueForKey:@"name"];
//

}
-(void)dealloc{[_p removeObserver:self forKeyPath:@"name"];
}
@end

三、底层原理探究

这里我总结三个地方

1.创建一个子类,名字是:NSKVONotifying_Person  ,person是本项目中的类

  这里为什么是子类不是分类呢,这里说明以下,如果使用分类 ,他会覆盖set方法,导致原set方法中的逻辑处理失效

2.重写了set方法

  这里的重写不是重写父类的的set,而是重写子类的

3.外界改变isa指针

  此处可以在注册方法打一个断点,观察其isa指针的变化

    self->_p->isa:Person改变为self->_p->isa:NSKVONotifying_Person

四、对容器的监听

对于数组,我们添加元素的时候,都是addObject........

但是我们知道,KVO是针对set方法从而监听的,因为,

addObject........是不会响应的,此时,苹果给我提供了

//    [_p.arry addObject:[NSString stringWithFormat:@"%d",a++]];//这一步不会触发监听方法因为监听是监听set的方法,addObject不是set方法//解决方法NSMutableArray *tenp =[_p mutableArrayValueForKey:@"arry"];[tenp addObject:[NSString stringWithFormat:@"%d",a++]];

五、自定义KVO

此处需要用到Runtime,KVO文档中,我们会发现,相关方法是在NSobjet的分类,所以

1.创建一个NSObject的分类,定义注册方法

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface NSObject (JK_KVO)
- (void)JK_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@endNS_ASSUME_NONNULL_END

2.实现该方法

#import "NSObject+JK_KVO.h"
#import <objc/message.h>@implementation NSObject (JK_KVO)- (void)JK_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{//   1.创建一个类 -- self.class 就是PersonNSString *oldname = NSStringFromClass(self.class);NSString *newNem = [@"JKKVO_" stringByAppendingString:oldname];Class myclass = objc_allocateClassPair(self.class, newNem.UTF8String, 0);// 注册类
    objc_registerClassPair(myclass);//    2.重写子类set方法 -- 所谓的重写就是给子类添加f这个方法 setName,因为子类没有父类的setName方法!!!/* class :给那个类添加方法*sel:方法编号*imp :方法实现(函数指针)*type :返回值类型*/class_addMethod(myclass, @selector(setName:), (IMP)setName, "v@:@");//    3.修改isa指针
    object_setClass(self, myclass);//    4.将观察保存到当前对象objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_ASSIGN);}void setName(id self,SEL _cmd,NSString *newName){NSLog(@"来了--%@",newName);
//    调用父类的setName方法Class class =[self class];object_setClass(self, class_getSuperclass(class));//改成父类
    objc_msgSend(self,@selector(setName:),newName);//发送消息给父类//    观察者id observer = objc_getAssociatedObject(self, @"observer");if (observer) {objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),@"name",self,@{@"new:":newName,@"kind:":@"1"},nil);}//    改回子类object_setClass(self, class);
}
@end

3.调用

 [_p JK_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

----!!!!!!!!

OK,结束。代码已经整理完毕。下班

转载于:https://www.cnblogs.com/henusyj-1314/p/10245363.html

KVO-基本使用方法-底层原理探究-自定义KVO-对容器类的监听相关推荐

  1. iOS底层原理探究 第一探. 事件传递和响应者链

    一. 声明:  本文意在探讨, 也参考了几位大神的文章, 在最后我会把链接发出来, 如果有理解错误的地方, 请大神们指正哈! 二. 前言:  最近自己做项目的时候, 用到了UITabbarContro ...

  2. web前端面试高频考点——Vue原理(理解MVVM模型、深度/监听data变化、监听数组变化、深入了解虚拟DOM)

    系列文章目录 内容 参考链接 Vue基本使用 Vue的基本使用(一文掌握Vue最基础的知识点) Vue通信和高级特性 Vue组件间的通信及高级特性(多种组件间的通信.自定义v-model.nextTi ...

  3. 自定义高效支持点击监听的RecyclerView

    自定义高效支持点击监听的RecyclerView 效果图 Demo 地址:GitHub 传统做法 在Adapter内部直接对View添加点击事件 因为这种方式虽然也可以解决点击监听问题,但是效率不高, ...

  4. iOS底层原理探究-Runloop

    Runloop 1. 概述 一般来说,一个线程只能执行一个任务,执行完就会退出,如果我们需要一种机制,让线程能随时处理时间但并不退出,那么 RunLoop 就是这样的一个机制.Runloop是事件接收 ...

  5. cpu缓冲区大小怎么设置_JAVA高薪面试必备知识点Volatile底层原理探究CPU在作怪

    基础知识回顾 还是那句话,无论语言再怎么牛,其都是对底层计算机指令的封装. 计算机CPU执行指令的时候是非常快的,如果每执行一个指令都从内存中取数据的话,那会非常慢,严重影响CPU的执行速度,所以每个 ...

  6. android gilde生命周期管理,Glide原理之Activity、Fragment生命周期监听(三)

    Glide中一个重要特性是Request可以随Activity或Fragment的onStart而resume,onStop而pause,onDestroy而clear,从而节约流量和内存,并且防止内 ...

  7. oc 协议 回调 静态成员_OC底层原理探究:Category、关联对象和block本质

    1.分类Category的使用 // 给MJPerson类添加分类 @interface MJPerson : NSObject - (void)run; @end@implementation MJ ...

  8. iOS之深入解析KVC的底层原理和自定义KVC的实现

    一.KVC 简介 ① 定义 KVC 是 Key-Value Coding 的简称,中文译义为键值编码. KVC 是指 iOS 的开发中,可以允许开发者通过 Key 名直接访问对象的属性,或者给对象的属 ...

  9. Runtime底层原理探究(二) --- 消息发送机制(慢速查找)

    源码 /*********************************************************************** * lookUpImpOrForward. * ...

最新文章

  1. evcdf matlab,求助大神
  2. 【registry】 javax.el.ExpressionFactory.newInstance()Ljavax/el/ExpressionFactory;
  3. linux文本模式无法打字,文本终端模式下中文输入法(Fcitx)配置(openSUSE Leap 42.1)...
  4. ContOS安装配置MySQL,redis
  5. excel用警员姓名查找警号信息
  6. 解决laravel框架中Eloquent ORM的save方法无法插入数据的问题
  7. MySQL新建用户,授权,删去用户,修改密码操作
  8. 操作系统实验一linux,操作系统实验一 Linux基本操作.doc
  9. dataframe在最后新增一行_【TOOLS】python中对Excel进行写入操作,写入一列或者一行(openpyxl)...
  10. StorageManager获取U盘挂载状态
  11. vue-cli3的eslint配置问题
  12. 请控制好你的情绪--职场情绪管理
  13. 矩形微带贴片天线设计
  14. D - New Year Snowmen(优先队列+贪心)
  15. 腾讯帝国15年争霸之路:谁说我只会抄袭,不会创新?
  16. 微信公众号最新留言评论管理功能怎么开通获取?(内附留言功能开通视频链接)
  17. 选对池塘钓大鱼([美]雷恩·吉尔森)第四章 想钓什么鱼?发现自己内在的需求...
  18. 文献管理软件Mendeley Reference Manager使用方法
  19. 汉堡王什么汉堡好吃_汉堡王什么汉堡好吃
  20. WIN11/win10+Azure Kinect DK详细驱动配置教程(亲测)

热门文章

  1. spring cloud学习进阶篇:Spring Cloud Sleuth + Zipkin 实现分布式跟踪解决方案
  2. Golang cgo编程 [] string 转 C语言 char**
  3. Idris趋近发布1.0版
  4. 模拟银行自助终端系统
  5. 生物-脑-脑容量:脑容量
  6. 一个关于Python字符串格式化输出的练习
  7. MySQL5.7 Group Replication (MGR)--Mysql的组复制之多主模式
  8. C#远程调用技术WebService葵花宝典
  9. 提升软件开发者效率的10个提示
  10. [原创]java WEB学习笔记94:Hibernate学习之路---session 的管理,Session 对象的生命周期与本地线程绑定...