博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
KVO-基本使用方法-底层原理探究-自定义KVO-对容器类的监听
阅读量:6199 次
发布时间:2019-06-21

本文共 4631 字,大约阅读时间需要 15 分钟。

书读百变,其义自见!

将KVO形式以代码实现呈现,通俗易懂,更容易掌握 :   -链接如果失效请自动搜索: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
*)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
*)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
*)change context:(void *)context{ NSLog(@"%@",change);}-(void)touchesBegan:(NSSet
*)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 
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 
@implementation NSObject (JK_KVO)- (void)JK_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{ // 1.创建一个类 -- self.class 就是Person NSString *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

你可能感兴趣的文章
微信开发第6章 通过accesstoken获取用户粉丝列表
查看>>
Linux 高可用(HA)集群之keepalived详解
查看>>
eclipse 比较好的插件
查看>>
Linux 信号详解四(pause,alarm)
查看>>
BAT面试的准备—iOS篇
查看>>
python selenium webdriver处理浏览器滚动条
查看>>
Linux 进程与线程五
查看>>
php字符集转换
查看>>
《你不知道的JavaScript》整理(五)——值与原生函数
查看>>
(五)hibernate关联映射之——多对一映射
查看>>
开源的报表系统easyreport的部署
查看>>
easyui combobox 的取值问题
查看>>
解决python:'ascii' codec can't encode characters in position问题
查看>>
NIO SelectionKey attachment()空指针错误
查看>>
不重复大数据量的批量生成
查看>>
sgu101-欧拉回路
查看>>
iOS_21团购_地图功能
查看>>
DOS批处理高级教程
查看>>
MySQL中OPTIMIZE TABLE的作用
查看>>
LeetCode211:Add and Search Word - Data structure design
查看>>