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<NSKeyValueChangeKey,id> *)change context:(void *)context{
     NSLog(@"keyPath:%@",keyPath);
     NSLog(@"ofObject:%@",object);
     NSLog(@"change:%@",change);
     NSLog(@"afterClass:%@", object_getClass(_stu));
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值