KVC,即Key-Value Coding,键值编码,简单地说,就是可以由key获取一个object对应的property。举个例子,如果一个对象object,它有一个属性item,你可以通过valueForKey也可以通过object.item来获取它,同时它支持纵调用,即假如object有个属性是个item,item有个属性score,可以通过@“item.score”获取,setValueForKey同理。

   CGFloat valueScore,score,valueTotal,total;
TestObject *object = [[TestObject alloc] init];
valueScore = [[object valueForKeyPath:@"item.score"] floatValue];
score = object.item.score;
valueTotal = [[object valueForKey:@"total"] floatValue];
total = object.total;

如果我们定义一个property,系统就已经默认帮我们实现了KVC,实现原理大家可以可以看看ios的message原理,之前已经介绍。至于它的好处,苹果官方给了一个例子,大家可以感受一下:

Using Key-Value Coding to Simplify Your Code
You can use key-value coding methods in your own code to generalize implementations. For example, in OS X NSTableView and NSOutlineView objects both associate an identifier string with each of their columns. By making this identifier the same as the key for the property you wish to display, you can significantly simplify your code.
Listing shows an implementation of an NSTableView delegate method without using key-value coding. Listing shows an implementation that takes advantage of key-value coding to return the appropriate value using the column identifier as the key. Listing Implementation of data-source method without key-value coding
- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row
{
ChildObject *child = [childrenArray objectAtIndex:row];
if ([[column identifier] isEqualToString:@"name"]) {
return [child name];
}
if ([[column identifier] isEqualToString:@"age"]) {
return [child age];
}
if ([[column identifier] isEqualToString:@"favoriteColor"]) {
return [child favoriteColor];
}
// And so on.
} Listing Implementation of data-source method with key-value coding

- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row {
ChildObject *child = [childrenArray objectAtIndex:row];
return [child valueForKey:[column identifier]];
}

在某种意义上,KVC是为了KVO的实现,下面重点说说KVO。KVO,即Key-Value Observing,也就是一个观察者模式,当一个对象的属性变化会收到对应的变化通知。变化的种类有四种,

typedef NS_OPTIONS(NSUInteger, NSKeyValueChange) {

    NSKeyValueChangeSetting = ,

    NSKeyValueChangeInsertion = ,

    NSKeyValueChangeRemoval = ,

    NSKeyValueChangeReplacement = 

};
其中NSKeyValueChangeSetting主要是对于一个对象地址的变化(或常量的变化),而后三个主要是对容器类的改变,举个例子,就是一个数组里新加一个item或者删掉一个item都会收到对应的通知,这就是kvo的强大之处了。
主要实现代码:
监听property:
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

keyPath就是对应的property的string,context就是允许自己携带的上下文,在回调里会返回。

移除property的监听:

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context

有一点必须注意,假如你没有监听这个property而调用移除,会导致系统崩溃。这里需要非常小心。

然后重写这个回调,当属性改变就会回调这个函数:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

keyPath就是对应的property,context就是监听手动设置的上下文,可以为NULL,字典里可以取到变化的type,原来的值和现在的值。

而要实现数组(或容器)里面item个数的变化,就要手动重写两个函数支持。下面举个例子:

(TestItem有一个数组array,需要实现监听这个数组里内容数据的变化)

@interface TestItem : NSObject

@property(nonatomic,retain)NSMutableArray *array;

@end

@implementation TestItem

-(void)insertObject:(id)object inArrayAtIndex:(NSUInteger)index //这个是代表property名字,就是上面定义的array,系统会自动生成,要根据自己定义的属性名字改变。

{

    [self.array insertObject:object atIndex:index];

}

-(void)removeObjectFromArrayAtIndex:(NSUInteger)index

{

    [self.array removeObjectAtIndex:index];

}
@end

需要特别主要的是,insertObject和removeObjectFromArrayAtIndex这两个函数都必须同时实现,否则是无效的。

这里只实现了这两个必须实现的函数,剩下的函数看功能需要:

-(void)insertArray:(NSArray *)array atIndexes:(NSIndexSet *)indexes{}

-(void)removeArrayAtIndexes:(NSIndexSet *)indexes{}

-(void)replaceArrayAtIndexes:(NSIndexSet*)indexes withArray:(NSArray *)array{}

-(void)replaceObjectInArrayAtIndex:(NSUInteger)index withObject:(id)object{}

当然,有点不方便的是,当我们要获取这个TestItem的array时,不能简单用item.array,而必须调用

  [[item mutableArrayValueForKey:@"array"] addObject:item];

只有用mutableArrayValueForKey得到的数组变化才会收到通知。

我们可以看看这样变化产生的change:

(lldb) po change
$ = 0x09169bc0 {
indexes = "<NSIndexSet: 0x9169a10>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = ;
new = (
"<TestItem: 0x7163cf0>"
);
}

indexes描述了变化的index,kind = 2即上面所说的NSKeyValueChangeInsertion,代表数组新插入了一个数据,new就是一个增加的数据数组。其他两种类型的变化大家可以感受一下。

至于NSmutableDictionary的实现原理一样,就是把array换成dictionary即可。

最后,KVO可以有依赖属性,即可以实现一个property可以影响其他property的变化而产生kvo的通知:

+(NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

{

    NSSet *set = [superkeyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"firstName"]) {

        NSArray *affectingKeys = @[@"fullName"];

        set = [set setByAddingObjectsFromArray:affectingKeys];

    }

    return set;

}

实现了如果firstName改变,也会产生fullName改变的通知。

仅供参考。欢迎指导。

KVC和KVO实现监听容器类(数组等)的变化的更多相关文章

  1. ios 利用kvc 监听可变数组变化

    KVO键值监听: Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知.简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观 ...

  2. 手动设定实例变量的KVO实现监听

    手动设定实例变量的KVO实现监听 如果将一个对象设定成属性,这个属性是自动支持KVO的,如果这个对象是一个实例变量,那么,这个KVO是需要我们自己来实现的. 以下给出源码供君测试: Student.h ...

  3. Android监听系统短信数据库变化-提取短信内容

    由于监听系统短信广播受到权限的限制,所以很多手机可能使用这种方式没法监听广播,从而没办法获取到系统短信,所以又重新开辟一条路. Android监听系统短信数据库内容变化使用场景: 1.监听短信数据库的 ...

  4. js监听输入框值的即时变化onpropertychange、oninput

    js监听输入框值的即时变化onpropertychange.oninput 很多情况下我们都会即时监听输入框值的变化,以便作出即时动作去引导浏览者增强网站的用户体验感. // //   要达到的效果 ...

  5. 监听INPUT值的即时变化

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. asp.net,监听输入框值的即时变化onpropertychange、oninput

    作者:自由天堂发布站点:WEB六零零 网页设计制作原文地址:http://www.web600.net/html/editor/JavaScript/201001131529.html 要达到的效果 ...

  7. vue中父组件如何监听子组件值的变化

    vue中我们会遇到很多父子组件通信的需求, 下面简单列一下,父子组件通信的几种情况 1:父组件向子组件传值:使用prop向子组件传值: 2:子组件实时监听父组件传来的值的变化:使用watch去监听父组 ...

  8. 怎样监听xhr.readyState值的变化

    可以使用 xhr.onreadystatechange 属性指向的函数去监听 xhr.readyState 值的变化. 示例如下: var xhr = new XMLHttpRequest(); xh ...

  9. ios中键值编码kvc和键值监听kvo的特性及详解

    总结: kvc键值编码  1.就是在oc中可以对属性进行动态读写(以往都是自己赋值属性)           2. 如果方法属性的关键字和需要数据中的关键字相同的话                  ...

随机推荐

  1. hibernate中使用sql语句进行表链接查询,对结果集的遍历方法

    今天做了一个在hibernate中使用sql语句进行表链接查询的功能,得到的属性是来自两个表中的字段.下面对结果集遍历的方法进行记录. sql语句不写了.部分代码如下: List<Course_ ...

  2. H264 编解码器架构简单

    看完后H264/AVC 编解码器演示,头脑是刚刚离开以下三个: 1.H264并且不明白如何指定的编解码器来实现,仅定义了一个编码视频位流的语法.和比特流进行解码,这与MPEG 类似. 2.H264而一 ...

  3. 【高德地图API】从零开始学高德JS API(一)地图展现——仙剑地图,麻点图,街景,室内图

    原文:[高德地图API]从零开始学高德JS API(一)地图展现——仙剑地图,麻点图,街景,室内图 摘要:关于地图的显示,我想大家最关心的就是麻点图,自定义底图的解决方案了吧.在过去,marker大于 ...

  4. 【甘道夫】Ubuntu群集配置 - 免费登陆

    引言 这是几年前写的文章,但一直以来该问题被反复问到.所以我决定将它又一次搬上屏幕. 正文 三个节点:masternode    slavenode1   slavenode2 第一步:全部节点分别生 ...

  5. 我的MYSQL学习心得(十)

    原文:我的MYSQL学习心得(十) 我的MYSQL学习心得(十) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(三) 我的MYSQL学习心得(四) 我的MYSQL ...

  6. Linux经常使用命令(一) - ls

    ls命令是linux下最经常使用的命令.ls命令就是list的缩写, 缺省下ls用来打印出当前文件夹的清单, 假设ls指定其它文件夹, 那么就会显示指定文件夹里的文件及文件夹清单. 通过ls 命令不仅 ...

  7. C语言库函数大全及应用实例十二

    原文:C语言库函数大全及应用实例十二                                          [编程资料]C语言库函数大全及应用实例十二 函数名: setrgbpalette ...

  8. 来选择一款适合你网站的CMS建站程序吧

    1:首页我们要搞清楚什么叫cms? ContentManagementSystem就是cms的全名,意思就是内容管理系统.cms整站系统是以文章系统为核心,增加用户需要的模块,如文章.图片.下载等,提 ...

  9. Web Service单元测试工具实例介绍之SoapUI

    原文  Web Service单元测试工具实例介绍之SoapUI SoapUI是当前比较简单实用的开源Web Service测试工具,提供桌面应用程序和IDE插件程序两种使用方式.能够快速构建项目和组 ...

  10. Dotfuscator自定义规则中的元素选择

    Dotfuscator是专业的.NET程序代码保护软件.是支持规则自定义的,你可以对重命名.程序控制流.字符串加密等等功能自定义规则.在进行规则自定义过程中,可以通过元素的不同选择,满足自己的程序需要 ...