KVC,KVO

作者: 旋叶 | 来源:发表于2018-03-16 17:15 被阅读0次

     KVC , KVO

    KVC和KVO的区别及应用

    KVC/KVO原理

    1. KVC键值编码

    KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。

    由于OC的语言特性,根本不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Value Coding(简称KVC)。

    KVC的操作方法由NSKeyValueCoding协议提供,而NSObject就实现了这个协议,也就是说ObjC中几乎所有的对象都支持KVC操作,常用的KVC操作方法

    动态设置 :

    setValue:属性值 forKey:key(属性名)(用于简单路径)

    setValue:属性值 forKeyPath:key(属性路径) (用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性)

    动态获取 :

    valueForKey:属性名

    valueForKeyPath:属性名(用于复合路径)

    注意

    key的值必须正确,如果拼写错误,会出现异常

    当key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来

    因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去

    例子

    一个对象拥有某些属性。比如说,一个 Person 对象有一个 name 和一个 address 属性。以 KVC 说法,Person 对象分别有一个 value 对应他的 name 和 address 的 key。 key 只是一个字符串,它对应的值可以是任意类型的对象。从最基础的层次上看,KVC 有两个方法:一个是设置 key 的值,另一个是获取 key 的值。如下面的例子:

    void changeName(Person *p, NSString *newName) {

    // using the KVC accessor(getter)method    

    NSString *originalName=[p valueForKey:@"name"];

    // using the KVC  accessor(setter)method.

    [p setValue:newName forKey:@"name"];

    NSLog(@"Changed %@'s name to: %@", originalName, newName);

    }

    现在,如果 Person 有另外一个 key 配偶(spouse),spouse 的 key 值是另一个 Person 对象,用 KVC 可以这样写:

    void logMarriage(Person *p){

    // just using the accessor again, same as example above    

    NSString *personsName=[p valueForKey:@"name"];

    // this line is different, because it is using    

    // a"key path"instead of a normal"key"

    NSString *spousesName=[p valueForKeyPath:@"spouse.name"];

    NSLog(@"%@ is happily married to %@", personsName, spousesName);

    }

    key 与 key pat 要区分开来,key 可以从一个对象中获取值,而 key path 可以将多个 key 用点号 "." 分割连接起来,比如:

    [p valueForKeyPath:@"spouse.name"];

    相当于这样……

    [[p valueForKey:@"spouse"]valueForKey:@"name"];

    KVC底层实现 :

    当一个对象调用setValue方法时,方法内部会做以下操作 :

    1>检查是否存在相应key的set方法,如果存在就调用set方法

    2>如果set方法不存在,就会查找与key相同名称并且带下划线的的成员变量,如果有,则直接赋值

    3>如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值

    4>如果还没有找到,则调用valueForUnderfindKey:和setValue: forUnderfindKey: 方法

    这些方法的实现默认都是抛出异常,我们可以根据需要重写他们。

    2. KVO键值监听

    KVO全称Key-Value Observing。

    典型的观察者模式承载者。

    基于监控键值发生变化,通知观察者。

    KVO 就是基于 KVC 实现的关键技术之一。

    KVO其实是一种观察者模式,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身。在ObjC中要实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所有的OC对象都可以使用KVO。

    在ObjC中使用KVO操作常用的方法如下:

    注册指定Key路径的监听器: addObserver: forKeyPath: options:  context:

    删除指定Key路径的监听器: removeObserver: forKeyPath、removeObserver: forKeyPath: context:

    回调监听: observeValueForKeyPath: ofObject: change: context:

    KVO的使用步骤也比较简单:

    通过addObserver: forKeyPath: options: context:为被监听对象(它通常是数据模型)注册监听器

    重写监听器的observeValueForKeyPath: ofObject: change: context:方法。

    KVO的底层实现 :

    1>KVO是基于runtime机制实现

    2>使用了isa混写(isa-swizzling),当一个对象(假设是person对象,person的类是MYPerson)的属性值(假设person的age)发生改变时,系统会自动生成一个类,继承自MYPerson : NSKVONotifying_MYPerson,在这个类的setAge方法里面,调用[super setAge : age]   [self willChangeValueForKey:@"age"] 和 [self didChangeValueForKey:@"age"] ,而这两个方法内部会主动调用监听者内部的 -(void)observeValueForKeyPath 这个方法。

    3>想要看到 NSKVONotifying_MYPerson很简答,在 self.person.age = 20;这里打断点,在调试区域就能看到 _person->NSObject->isa=(Class)NSKVONotifying_MYPerson.  同时我们在 self.person = [[MYPerson alloc]init]; 后面打断点,看到  _person->NSObject- >isa=(Class)MYPerson,  由此可见,在添加监听者之后,person 类型已经由 MYPerson 被改变为 NSKVONotifying_MYPerson

    相关文章

      网友评论

          本文标题:KVC,KVO

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