KVO与KVC

作者: 滚滚猫 | 来源:发表于2019-08-16 17:55 被阅读0次

    KVC是什么

    KVC 全称Key-Value Coding,俗称"键值编码"。它可以通过一个字符串(key)来访问属性或者成员变量。

    • 在苹果的官方文档中是这样描述KVC的:它是一种通过字符串描述符而不是通过调用访问方法或者直接使用实例变量的非直接的访问对象属性的机制。
    • 对于KVC的基本的方法都定义在NSKeyValueCoding的非正式协议中,并且NSObject默认实现了该协议。
    • KVC不仅支持对象类型,也支持数值类型和结构体。非对象类型的参数和返回类型会自动封装成NSValue或NSNumber类型。

    KVC常用api

    //赋值
    -(void)setValue:(id)value forKeyPath:(NSString *)keyPath; 
    -(void)setValue:(id)value forKey:(NSString *)key;
    
    //取值
    -(id)valueForKeyPath:(NSSting *)keyPath;
    -(id)valueForKey:(NSSting *)key;
    

    KVC原理

    kvc在取值和赋值的时候是按照一定的顺序进行查找的

    赋值原理

    赋值原理图.png
    取值原理
    取值原理图.png

    注意:
    1、kvc在赋值和取值的时候是按照顺序进行的,可以自己建一个类把对应方法写进去看下执行及顺序。
    2、赋值时如果找不到成员变量,不实现setValue:forUndefinedKey:方法会崩溃
    如果成员变量是int等基本数据类型,会自动包装成NSNumber类型返回
    3、取值时如果找不到成员变量,不实现valueForUndefinedKey方法会崩溃
    如果成员变量是int等基本数据类型,赋值nil,不实现setNilValueForKey:会崩溃

    KVO是什么

    KVO 全称Key-Value Observing,俗称"键值监听",可以用于监听某个对象属性值的改变

    KVC常用api

    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
    

    KVC原理

    栗子:创建一个MJPerson类的对象,添加监听

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.person1 = [[MJPerson alloc] init];
        self.person1.age = 1;
        // 给person1对象添加KVO监听
        NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
        [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        self.person1.age = 20;
    }
    
    // 当监听对象的属性值发生改变时,就会调用
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
    }
    
    - (void)dealloc {
        //移除监听
        [self.person1 removeObserver:self forKeyPath:@"age"];
    }
    
    

    OC在运行时的时候自动生成了一个NSKVONotifying_MJPerson 类对象作为MJPerson类对象的子类对象,通过底层_NSSet**ValueAndNotify方法(这里是_NSSetIntValueAndNotify) 重写了setAge:方法,进而实现KVO的监听过程。

    以下可理解为实现该过程的伪代码

    #import "MJPerson.h"
    @interface NSKVONotifying_MJPerson : MJPerson
    @end
    
    #import "NSKVONotifying_MJPerson.h"
    @implementation NSKVONotifying_MJPerson
    
    - (void)setAge:(int)age
    {
        _NSSetIntValueAndNotify();
    }
    
    // 伪代码
    void _NSSetIntValueAndNotify()
    {
        [self willChangeValueForKey:@"age"];
        [super setAge:age];
        [self didChangeValueForKey:@"age"];
    }
    
    - (void)didChangeValueForKey:(NSString *)key
    {
        // 通知监听器,某某属性值发生了改变
        [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
    }
    

    监听过程图

    KVO监听过程图.png

    验证过程在 我的参考文献1进行了很详细的说明

    KVO监听数组

    iOS默认不支持KVO的形式来监听数组的变化,当我们想要使用KVO监听数组的状态时,要做到两点:
    1、要将监听的数组封装到model里,不能监听UIViewController里面的数组
    2、不能这样[_model.modelArray addObject]方法,需要这样调用
    [[_model mutableArrayValueForKey:@"modelArray"] addObject:str];

    @interface ObserverModel : NSObject
    @property (strong,nonatomic)NSMutableArray *dataArray;
    @end
    
    @implementation ObserverModel
    -(NSMutableArray *)dataArray{
        if(!_dataArray){
            _dataArray = [NSMutableArray array];
        }
        return _dataArray;
    }
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.observerModel = [[ObserverModel alloc] init];
        [self.observerModel addObserver:self forKeyPath:@"dataArray" options:NSKeyValueObservingOptionNew context:nil];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        [[self.observerModel mutableArrayValueForKeyPath:@"dataArray"] addObject:@"haha"];
    //    [[obserVer mutableArrayValueForKeyPath:@"dataArray"] removeObject:voiceContent];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"dataArray"]){
            NSLog(@"%@",change[@"new"]);
        }
    }
    
    -(void)dealloc{
        if (self.observerModel != nil) {
            [self.observerModel removeObserver:self forKeyPath:@"dataArray"];
        }
    }
    

    参考文档
    https://www.jianshu.com/p/8a9cd3d1bed7
    https://www.jianshu.com/p/4eae365accc1
    https://www.jianshu.com/p/f27d060fc1f1

    相关文章

      网友评论

          本文标题:KVO与KVC

          本文链接:https://www.haomeiwen.com/subject/dbzfsctx.html