KVO 的使用和举例
KVO(key-value Observer),通过命名可以联想到,一个监视着监视着键值配对,让一个对象A来监视另一个对象B中的键值,一旦B中的受监视键所对应的值发生了变化,对象A会进入一个回调函数,有机会对于B中的受监视键值的改变立刻进行处理和应对。
注:虽然对象A中的回调函数有点像代理方法,但是回调函数的调用和键值发生变化处在同一个线程中,并非像某些代理方法会在另一个线程中进行回调。也就是说,如果对键key进行了监视,一旦键key对应的值发生了变化,就会去调用监视着的回调函数,直到回调函数跑完后键key对应值发生变化的流程才能继续。
好处就是减少胶水代码。
比如比赛比分发生了变化,如果我们不用KVO机制,我们需要告诉大屏幕控制人员,告诉网络媒体,告诉广播电台播音员,甚至告诉其他赛场的工作人员。
一个简单的KVO机制的程序
导航栏有三个元素,左边的编辑按钮,用来删除表的记录,右边的“+”按钮,用来新增表的记录,而当中的标题,用来显示最近的一次动作。开发思路大致为这样:
表视图有一个数据源dataSource,我们需要利用kVO机制去监视这个数据源,当按下“+”按钮时往数据源中添加一条数据,触发KVO,随后在KVO的回调函数中,我们将界面更新成和数据源同步。
当删除一条数据时,数据源减少一条数据,同样触发KVO并在随后KVO的回调函数中,将界面更新同步。
总体来说,无论对数据源做任何操作,我们都会在KVO的回调函数中,进行程序界面和数据源的同步工作,代码如下:
@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
{
IBOutlet UITableView *_tbv;
} //遵循KVC的编码规范
@property (nonatomic,retain) NSMutableArray *dataSrc;
@property (nonatomic,retain) NSString *titleMsg; //提供KVC中对于容器键属性(dataSrc)的接口
-(NSUInteger)countOfDataSrc;
-(void)insertObject:(id)object inDataSrcAtIndex:(NSUInteger)index;
-(id)objectInDataSrcAtIndex:(NSUInteger)index;
-(void)removeObjectFromDataSrcAtIndex:(NSUInteger)index;
@end
上述代码中一共声明了两个属性变量:dataSrc作为数据源,titleMsg作为标题
由于数据源dataSrc是属于容器类型的数据,根据KVC协议需要申明并实现数组形式的几个方法
协议的时间内容中直接使用可变数组提供的功能,对上述四个接口进行实现,代码如下:
//集合属性的个数
-(NSUInteger)countOfDataSrc
{
return [self.dataSrc count];
} //集合属性的新增动作
-(void)insertObject:(id)object inDataSrcAtIndex:(NSUInteger)index
{
[self.dataSrc insertObject:object atIndex:index];
} //集合属性的取值动作
-(id)objectInDataSrcAtIndex:(NSUInteger)index
{
return [self.dataSrc objectAtIndex:index];
} //集合属性的删除动作
-(void)removeObjectFromDataSrcAtIndex:(NSUInteger)index
{
[self.dataSrc removeObjectAtIndex:index];
}
至此KVC的准备工作都做完了,继续实现KVO机制,对于界面的初始化进口位置,作如下初始化的设置
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//初始化表视图(UITableView)的数据
self.dataSrc = [[NSMutableArray alloc]initWithCapacity:0];
self.titleMsg = @"没有动作";
_tbv=[[UITableView alloc]init];
//
//对表视图的数据进行监视
//
//谁来监视,KVO的监视回调函数就调用谁
[self addObserver:self
//监视的键的路径,我们这里的属性由于只有一层,所以直接写dataSrc
forKeyPath:@"dataSrc"
//需要知道表数据改动时的新旧数据,方便我们研究,如果不需要,可以置为0
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
//KVO 触发时,我们收到的额外信息,如果不需要可以置为nil
context:@"testContent"]; [self addObserver:self forKeyPath:@"titleMsg" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"testContent"]; //右边的按钮,我们放增加
UIBarButtonItem *addButton=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add)];
self.navigationItem.rightBarButtonItem=addButton; //左边的按钮,我们放编辑,主要提供删除功能
//初始化没有数据,所以我们disable掉“编辑”按钮
self.navigationItem.leftBarButtonItem=self.editButtonItem;
self.navigationItem.leftBarButtonItem.enabled=NO;
self.editButtonItem.title=@"编辑"; //标题
self.navigationItem.title=self.titleMsg; _tbv.delegate=self;
[self.view addSubview:_tbv];
}
然后写上必须释放的方法
-(void)dealloc
{
[self removeObserver:self forKeyPath:@"dataSrc"];
[self removeObserver:self forKeyPath:@"titleMsg"];
}
随后当用户点击“+”按钮时,新增的处理函数如下:
//导航栏上增加按钮的调用方法
-(void)add
{
//我们打算设置一个静态的整形记录当前的排序值
static int myIndex=; //每次进来,我们就把当前的排序值作为新增的对象
//所以调用KVO提供的新增接口,插入新元素的位置始终位于最后
[self insertObject:[NSString stringWithFormat:@"%d",myIndex] inDataSrcAtIndex:[self countOfDataSrc]]; myIndex++; self.titleMsg=[NSString stringWithFormat:@"新增:%d",myIndex];
}
当用户点击“编辑”按钮时,被调用的系统默认的方法进行重写
//当用户单击“编辑”按钮时,对被调用的系统默认方法进行重写
-(void)setEditing:(BOOL)editing animated:(BOOL)animated
{
//UIViewController 提供的editButtonItem 默认会调用此方法
//所以我们重写此方法,第一步就是让表视图变成编辑状态,供我们删除内容用 [_tbv setEditing:editing animated:animated]; //第二步让super继续操作
//目的是不改变UIViewController对于editButtonItem原有的动作
//如果不加,那就是等于我们将这个方法截获了
//效果不同体现在:editButtonItem不会在Edit状态和Done状态之间切换
[super setEditing:editing animated:animated]; if(editing)
{
self.editButtonItem.title=@"完成";
}
else
{
self.editButtonItem.title=@"编辑";
}
}
当用户按下“Delete”后,作为表视图的代理,“tableView:commitEditingStyle:forRowAtIndexPath:”,这个代理方法将会被调用,所以需要实现如下代码:
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(editingStyle==UITableViewCellEditingStyleDelete)
{
self.titleMsg=[NSString stringWithFormat:@"删除:[%d]",indexPath.row];
[self removeObjectFromDataSrcAtIndex:indexPath.row];
}
}
KVO所触发的回调函数的实现方式
//KVO监视某个属性时,当属性发生变化会受到此回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:@"titleMsg"])
{
[self handleTitleChangeofObject:object
change:change
context:context];
return;
} NSInteger changeRow=;
//NSKeyValueChangeIndexesKey键中记录了集合属性改变位置等重要信息
NSIndexSet *indices=[change objectForKey:NSKeyValueChangeIndexesKey]; if(indices)
{
//我们每次只改集合中的一处地方,所以我们可以用firstIndex来简单的取出改变的地方
//如果时多处地方遭到修改,需要使用NSindexSet类提供的getIndexes方法
changeRow=indices.firstIndex;
} //制作NSIndexPath,为了提供给表视图进行UI更新
NSIndexPath *changeIndexPath=[NSIndexPath indexPathForRow:changeRow inSection:]; //NSKeyValueChangeKindKey信息中记录了监视属性的值变化类型
NSNumber *kind=[change objectForKey:NSKeyValueChangeKindKey];
switch ([kind intValue]) {
case NSKeyValueChangeInsertion:
//此新增方法后,表视图重绘
[_tbv insertRowsAtIndexPaths:[NSArray arrayWithObjects:changeIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSKeyValueChangeRemoval:
//次删除方法后,表视图会重绘
[_tbv deleteRowsAtIndexPaths:[NSArray arrayWithObjects:changeIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
default:
break;
} //控制编辑按钮
//如果表数据有记录
if([self countOfDataSrc]>)
{
//让编辑按钮可用
self.navigationItem.leftBarButtonItem.enabled=YES;
}
else
{
//让编辑按钮不可用,并且遵循UIVievController对于不可用时的UI处理(比如变成edit)
[self setEditing:NO animated:YES];
self.navigationItem.leftBarButtonItem.enabled=NO;
} }
下列代码则是对于界面标题的更新代码
-(void)handleTitleChangeofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
self.navigationItem.title=self.titleMsg;
}
剩下的表视图的实现方法就不贴了
KVO 的使用和举例的更多相关文章
- kvo深入浅出举例
一,概述 KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知.简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知 ...
- KVC 和 KVO
KVC 键值编码 全称是Key-value coding,翻译成键值编码.它提供了一种使用字符串而不是访问器方法去访问一个对象实例变量的机制. 1.通过key(成员变量的名称)设置 ...
- KVC/KVO原理详解及编程指南
一.简介 1.KVC简介 2.KVO简介 二.KVC相关技术 1.Key和Key Path 2.点语法和KVC 3.一对多关系(To-Many)中的集合访问器方法 4.键值验证(Key-Value V ...
- iOS-KVC和KVO精炼讲解(干货)
一.KVO介绍 KVO就是观察者模式,说白了就是你关心的一个值改变了,你就会得到通知.你就可以在你想处理的地方处理这个值. 二.KVO的使用 一般分为三步: 注册监听 使用方法: /** * 添加KV ...
- iOS中关于KVC与KVO知识点
iOS中关于KVC与KVO知识点 iOS中关于KVC与KVO知识点 一.简介 KVC/KVO是观察者模式的一种实现,在Cocoa中是以被万物之源NSObject类实现的NSKeyValueCodin ...
- runtime/KVO等面试题
整理中... 1.KVO内部实现原则 回答:1>KVO是基于runtime机制实现的 2>当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中 ...
- 【转】 KVC/KVO原理详解及编程指南
原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 前言: 1.本文基本不讲KVC/KVO的用法,只结合网上的资料说说对这种技术的 ...
- 转:KVC/KVO原理详解及编程指南
作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 转载请注明出处 如果觉得文章对你有所帮助,请通过留言或 ...
- KVO 进阶
Key-value coding (KVC) 和 key-value observing (KVO) 是两种能让我们驾驭 Objective-C 动态特性并简化代码的机制.在这篇文章里,我们将接触一些 ...
随机推荐
- 【翻译】在Ext JS 5种使用ViewControllers
原文:Using ViewControllers in Ext JS 5 简单介绍 在Ext JS 5中,在应用程序架构方面提供了一些令人兴奋的改进,如加入了ViewModels.MVVM以及view ...
- cmd命令 拷贝某文件夹及其子文件夹文件到其它文件夹
@ECHO OFF cd/d %H:\FileLoc\CNET&cd.. ::echo 拷贝"%H:\FileLoc\CNET"中文件到"H:\FileLocTe ...
- CodeForces Round #179 (295A) - Greg and Array 一个线段树做两次用
线段树的区间更新与区间求和...一颗这样的线段树用两次... 先扫描1~k...用线段树统计出每个操作执行的次数... 那么每个操作就变成了 op. l , op.r , op.c= times* ...
- Orcale语句大全
原文地址:http://www.cnblogs.com/omygod/archive/2007/08/31/876620.html Oracle 语句大全 1. Oracle安装完成后的初始口令? ...
- android上传位置信息导致的流量大爆炸问题调查
原由:项目中有人写了个位置上传的服务,其实一直没问题,后来不知道什么时候出现了很多抱怨,是开着app流量一下子跑掉了几个G,差点就要卖房子还移动话费了,很多同事哭笑不得的找上门来,后来PM解决了,我一 ...
- English - 定冠词和不定冠词(a an the) 的区别
不定冠词表示泛指,定冠词表示特指. 不定冠词a (an)与数词one 同源,是"一个"的意思.a用于辅音音素前,一般读作[e],而an则用于元音音素前,一般读做[en]. 1) 表 ...
- poj2987 Firing
以前只是A过很简单的最大闭合权像hdu1565之类,完全的最大流模板题.但是都完全不太懂最大闭合权的定义及其用途. 关于最大流的基础知识,大家可以自己网上搜索关键字.有点基础的哥们妹们,推荐看看胡伯涛 ...
- 基础知识——Cocos2d-x学习历程(三)
1.场景与流程控制 我们把一些内容相对不变的游戏元素集合称作场景(scene),把游戏在场景之间切换的过程叫做流程控制(flow control). 在Cocos2d-x中,场景的实现是Scene. ...
- android操作ini工具类
package com.smarteye.common; import java.io.BufferedReader; import java.io.BufferedWriter; import ja ...
- BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )
dp(i) = max(dp(i-1), x[j]*a[i]+y[j]*b[i]), 0<j<i. x, y表示某天拥有的最多钱去买金券, 金券a和金券b的数量. 然后就很明显了...平衡 ...