KVO和KVC

 

KVC:

  • KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过key直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存储方法。这样就可以在运行时动态地访问和修改对象的属性。

  • 在kvc当中有常用的4种方法:                  
- (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

 1)kvc设值

          在kvc设值是,要指出设值的属性,kvc需要在对象的内部来查找属性,这个流程的顺序为:

              1.首先寻找set函数,先找setKey函数,如果不存在,就去寻找_setKey函数。

              2.如果没有找到1中所述的函数,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,当然也可以重写这个函数,让它返回NO,如果返回NO,会立即调用setValue:forUndefinedKey:方法,不过一般不会这样做,因为kvc可以直接向属性赋值而不通过set函数去实现,即不管是public修饰的还是private修饰,都可以直接赋值。

              3.当开始直接查找属性时,KVC会按照    _Key,_isKey,Key,isKey,  这样的优先级去赋值;

              4.当上述的方法和属性都不存在是,会调用setValue:forUndefinedKey:方法。此方法默认是抛出异常。

         

           简单来说就是如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员并进行赋值操作。如果开发者想让这个类禁用KVC里,那么重写+(BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set:属性名时,会直接用setValue:forUndefinedKey:方法。
 

 2)kvc取值         

      首先会按照getKey,Key,isKey来进行getter方法查找,若存在就赋值;

如果getter方法都没有找到,KVC机制就会查找countOfKey 、objectInKeyAtIndex 、KeyAtindex、getKey :range 这些方法,若上面的几个方法任一个被找到,那么就会返回一个NSKeyValueArray(含有所有的方法的代理集合),以上面的几个方法组合形式调用;

如果上面的countOfKey 、objectInKeyAtIndex 、KeyAtindex、getKey :range 这些方法没有找到 ,会去查找 countOfKey、enumeratorOfKey、memberOfKey格式的方法,若找到这三个方法会返回一个可以相应NSSet所有方法的代理集合,会以countOfKey、enumeratorOfKey、memberOfKey组合形式调用;

如果都没有找到,就去查找+ (BOOL)accessInstanceVariablesDirectly,如果返回YES,就和设值一样的执行机制。直接查找属性,按照_key,_iskey,key,iskey的顺序搜索成员。

 

KVC使用keypath

当属性为自己定义的类或者复杂的数据类型时,我们可以先获得这个成员,然后再次用KVC访问该成员的属性。但是这样比较繁琐,所以为了方便,我们可以使用 键路径,即KeyPath。

 

 KVC处理异常

KVC中最常见的异常就是不小心使用了错误的key,或者在设值中不小心传递了nil的值,KVC中有专门的方法来处理这些异常。

通常情况下,KVC不允许你要在调用setValue:属性值 forKey:(或者keyPath)时对非对象传递一个nil的值。很简单,因为值类型是不能为nil的。如果你不小心传了,KVC会调用setNilValueForKey:方法。这个方法默认是抛出异常,所以一般而言最好还是重写这个方法。

KVC处理集合

简单集合运算符

简单集合运算符共有@avg, @count , @max , @min ,@sum5种

 Person *p1=[[Person alloc ] init];

    p1->_age=10;

    Person *p2=[[Person alloc ] init];

    p2->_age=10;

    Person *p3=[[Person alloc ] init];

    p3->_age=10;

    Person *p4=[[Person alloc ] init];

    p4->_age=10;

    Person *p5=[[Person alloc ] init];

    p5->_age=60;

    

    NSArray *arr=@[p1,p2,p3,p4,p5];

    NSNumber* sum = [arr valueForKeyPath:@"@sum.age"];

    NSLog(@"%@",sum);

    NSNumber* avg = [arr valueForKeyPath:@"@avg.age"];

    NSLog(@"%@",avg);

    NSNumber* count = [arr valueForKeyPath:@"@count"];

    NSLog(@"%@",count);

 

2022-05-03 20:49:22.287921+0800 A7d[2483:85475] 100

2022-05-03 20:49:22.288128+0800 A7d[2483:85475] 20

2022-05-03 20:49:22.288594+0800 A7d[2483:85475] 5


KVO:

     

KVO(key-value observe)是在KVC的基础上实现的一种用于监听属性变化的设计模式;如果对某个类的某个属性设置了KVO,那么当这个属性发生变化时,就会触发监听方法,从而知道属性变化了。如果本类一个属性的改变会影响到其他多个属性的变化,我们也会经常自己重写这个属性的set方法,用来监听他的变化,但是如果不是本类的属性,我们就没办法重写其set方法了,这个时候KVO就可以上场了,其实KVO本质上也是重写set方法,而整个过程依赖于runtime才能实现。

 

1.添加监听者

[p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld context:nil];

p1  被监听者

self   监听者

options  枚举类型,选择要保留的数据,本例保存了旧数据。默认保留了新数据

 

2.实现监听回调方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context

  {

      NSLog(@"改变,,,%@",change[@"old"]);

 }

当被监听的属性被改变时,会自动调用回调方法

 

3、实现原理

  KVO是基于runtime实现的,在添加监听者时,runtime会动态生成一个类,此时实例对象的isa指针不在指向Person类对象,而是指向生成的类。并重写我们监听的属性(keyPath)的set方法

同时也重写了class函数,此时调用class仍然会返回Person类对象,而用object_getclass 会返回此时isa指向的类,即动态生成的类。

 Person *p1=[[Person alloc ] init];

Person *p2=[[Person alloc ] init];

 

     Class cls1 =object_getClass(p1);

      Class cls2 =object_getClass(p2);

      NSLog(@"添加监听前%@    %@",cls1,cls2);

      //监听p1

      [p1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld context:nil];

    //改变p1的属性

      [p1 setValue:@"jack" forKey:@"name"];

 

    cls1 =object_getClass(p1);

    cls2 =object_getClass(p2);

    NSLog(@"添加监听后%@    %@",cls1,cls2);

 

 

 

 

2022-05-03 22:12:31.992420+0800 A7d[3280:137055] 添加监听前Person    Person

2022-05-03 22:12:31.992831+0800 A7d[3280:137055] 改变,,,

2022-05-03 22:12:31.992964+0800 A7d[3280:137055] 添加监听后NSKVONotifying_Person    Person

 

KVO是在KVC的基础上实现的,所以只有通过kvc来修改属性的值才会触发kvo,自己直接改变属性值则不会触发。

posted @   A7d  阅读(205)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示