KVC与KVO

来源:互联网 发布:如何更新mac os系统 编辑:程序博客网 时间:2024/06/12 01:10

1. 概述

ObjC主要基于Smalltalk进行设计, 因此它有很多类似Ruby,Python的动态特性, 例如动态类型,动态加载,动态绑定等. 因此可以ObjC中可以使用键值编码KVVC 和 键值监听KVO; 基于观察者思想:

一个目标对象 管理所有依赖于它的 观察者对象;并在自身的状态改变时 主动通知观察者对象. 通知通告调用各观察着对象所提供的接口方法实现, 观察者模式 为了解耦;

1. KVC

C#中可以通过反射读写一个对象的属性, 利用字符串的方式去动态控制一个对象. 但是对于ObjC的runtime特性, 我们不需要进行任何操作即可进行属性的动态读写,

KVC的操作方法有NSKeyValueCoding协议提供, NSObject遵守了这个协议, 所以OC的对象都可以使用KVC;

  • 动态设置: setValue: forKey:属性名 (用于简单路径) / setValue: forKeyPath:属性路径(用于复合路径,即属性的某属性.例如Person有一个Account类型的属性,那么person.account就是一个复合属性)其实就是 属性链式访问
  • 动态读取: valueForKey: 属性名 / valueForKeyPath: 属性路径

注意:

  1. KVC 可以访问私有变量.
  2. valueForKey会自动把基本类型转成NSNumber或NSValue中包装成对象,同样动态设置setValue: forKey:的属性也必须先包装成NSNumber对象类型才可以.

查找规律:

  1. 先检查是否存在属性a的set和get方法(BOOL类型属性的get方法名是is), 没有就会搜索_/_set方法.
  2. 如果还没有再搜索成员变量_a, 如果仍不存在,就会搜索成员变量a
  3. 如果最后仍没搜索到, 会根据设值还是取值 调用 setValue:forUndefinedKey:或valueforUndefineKey: 抛出异常.根据需要重写它们;

补充

批处理:

KVC可以对对象进行批量更改,dictionaryWithValuesForKeys:和setValuesForKeysWithDictionary:dict;

数组的整体操作:

如果向一个数组请求一个key,KVC会查询数组中每个对象来查找这个key,之后会将结果打包到一个新数组并返回;例:student有很多book, 获取book的nameNSArray *names = [student valueForKeyPath:@"books.name"];

键路径的运算符:

在路径中,可以引用一下运算符@xxxxx来进行一些运算,例如获取一组值得平均值,最值或者总数.

  1. 简单运算符@avg @count @max @min @sum
  2. 对象运算符: @distinctUnionOfObjects(去掉重复) @unionOfObjects(不去重复),都返回数组
  3. Array和Set操作符: 集合中包含集合的情况.
NSNumber *count = [student valueForKeyPath:@"books.@count"];//计算总数NSNumber *sum = [student valueForKeyPath:@"books.@sum.price"];//总和NSArray *prices = [student valueForKeyPath:@"books.@distinctUnionOfObjects.price"]; 

KVO

//1. 注册监听器-(void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)contextanObserver :监听器对象keyPath :监听的属性options :决定了当属性改变时,要传递什么数据给监听器//2.监听器需要实现监听方法,来处理收到的通知;-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)contextkeyPath :监听的属性object :谁的属性改变了change :属性改变时传递过来的信息(取决于添加监听器时的options参数//3. 最后移除监听器-(void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath

注册和解除注册定义在NSKeyValueObserving协议中, NSObject,NSArray,NSSet实现了此协议;

KVO实现机制:

当某个类的对象第一次被观察时,系统就会在 运行时动态的创建该类的一个派生类, 在这个派生类中重写原类中被观察属性的setter方法;
派生类在被重写的setter方法中实现真正的 通知机制. 这是基于设置属性会调用setter方法,而通过重写就可以获得KVO需要的通知机制. (所以,使用KVO要遵循其属性设置方式来改变属性值, 如果仅仅直接修改属性值,是无法实现KVO的); (补充: Swift中的属性观察器原理相似)

- (void) setAge:(int)theAge{    // will和did两个方法用于通知系统该key的属性值即将和已经变更    [self willChangeValueForKey:@"age"];    age = theAge;    [self didChangeValueForKey:@"age"]; }//didChangeValueForKey方法会调用下面方法,+(BOOL) automaticallyNotifiesObserversForKey:(NSString *)key {    if ([key isEqualToString:@"age"]) {        return NO;    }    return [super automaticallyNotifiesObserversForKey:key];}

同时派生类还重写了class方法以”欺骗”外部调用者它就是起初的那个类.然后系统将这个对象的isa指针指向了这个新诞生的派生类, 之后调用该对象的setter就会调用重写后的setter从而激活通知
机制.当然,派生类还重写了dealloc方法来释放资源.

总结: KVO的三种方式:
  1. 使用了KVC:如果有访问器方法,则运行时会在访问器方法中调用will/didChangeValueForKey:方法; 如果没有访问器方法,运行时会在setValue:forKey方法中调用will/didChangeValueForKey:方法;
  2. 有访问器方法: 运行时会重写访问器方法来调用will/didChangeValueForKey:;
  3. 如果没有使用KVC,且没有访问器方法, 可以显示调用will/didChangeValueForKey:.就同样可以使用KVO;
0 0
原创粉丝点击