码迷,mamicode.com
首页 > 其他好文 > 详细

KVO 使用及原理

时间:2018-01-18 23:08:13      阅读:233      评论:0      收藏:0      [点我收藏+]

标签:创建   end   方法   div   kvc   option   tostring   obs   locate   

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, 0);
     //注册类
    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 使用及原理

标签:创建   end   方法   div   kvc   option   tostring   obs   locate   

原文地址:https://www.cnblogs.com/daxueshan/p/8313318.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!