本文转自:http://www.2cto.com/kf/201401/274441.html
OS中KVO,KVC的学习记录
KVO
#import <foundation foundation.h="">
#import "BankAccount.h"
@interface Person : NSObject
{
BankAccount *bankAccount;
}
- (void)registerAsObserver;
@end
#import "Person.h"
@implementation Person
- (void)dealloc{
bankAccount = nil;
}
- (id)init{
self = [super init];
if (self) {
bankAccount = [[BankAccount alloc]init];
}
return self;
}
//OpeningBalance 指向自己的指针
static void *OpeningBalance = (void *)&OpeningBalance;
- (void)registerAsObserver{
//监听银行账号的变化过程
[bankAccount addObserver:self forKeyPath:@"openingBalance" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:OpeningBalance];
//给银行账号bankAccount 增加一个监听者 self,监听openingBalance的变化过程
//只要openingBalance有变化,就会让self知道
//只要有变化,只要有新的值
}
- (void)unregisterObserver{
//self从bankAccount 中解除监听对象
[bankAccount removeObserver:self forKeyPath:@"openingBalance"];
}
//监听回调的函数
//bankAccount 里面openingBalance 有变化了,就会调用下面的方法
//keyPath 表示之前监听的key 也就是openingBalance
//object 表示bankAccount
//change 字典 包含了新,旧的值
//context是私有变量OpeningBalance
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
// [keyPath isEqualToString:@"openingBalance"]
if (context == OpeningBalance) {
NSString *v = [change objectForKey:NSKeyValueChangeNewKey];
NSLog(@"v is %@",v);
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end
</foundation>
#import <foundation foundation.h="">
@interface BankAccount : NSObject
{
float _openingBalance;
}
//账号余额
@property(nonatomic,assign)float openingBalance;
@end
#import "BankAccount.h"
@implementation BankAccount
@synthesize openingBalance = _openingBalance;
- (id)init{
self = [super init];
if (self) {
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(balanceUpdate:) userInfo:nil repeats:YES];
}
return self;
}
- (void)balanceUpdate:(id)arg
{
float f = self.openingBalance;
f += arc4random()%100;
// _openingBalance = f;不能这么写
//1.
//self.openingBalance = f;
//2.
//[self setOpeningBalance:f];
//3.
//[self setValue:[NSNumber numberWithFloat:f] forKey:@"openingBalance"];
//4.
[self willChangeValueForKey:@"openingBalance"];
_openingBalance = f;
[self didChangeValueForKey:@"openingBalance"];
}
@end
</foundation>
KVC
PlayList
#import <foundation foundation.h="">
#import "PlayItem.h"
@interface PlayList : NSObject
{
int _number;
NSString *_name;
//当前播放列表
PlayItem *_currItem;
NSMutableArray *_itemList;
}
@property(nonatomic, strong)NSMutableArray *itemList;
@property(nonatomic, assign)int number;
@property(nonatomic, strong)NSString *name;
@property(nonatomic, strong)PlayItem *currItem;
@end
</foundation>
#import "PlayList.h"
@implementation PlayList
@synthesize number = _number, name = _name,currItem = _currItem,itemList = _itemList;
- (id)init{
self = [super init];
if (self) {
self.currItem = [[PlayItem alloc]init];
self.itemList = [NSMutableArray array];
for (int i = 0; i < 20; i++) {
PlayItem *pi = [[PlayItem alloc]init];
pi.name = [NSString stringWithFormat:@"name %d",i];
pi.price = 100+i;
[self.itemList addObject:pi];
}
}
return self;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
NSLog(@"file is %s function %@ is calling",__FILE__,NSStringFromSelector(_cmd));
}
- (void)dealloc{
self.name = nil;
self.currItem = nil;
self.itemList = nil;
}
@end
PlayItem
@interface PlayItem : NSObject
{
NSString *_name;
float _price;
}
@property(nonatomic, strong)NSString *name;
@property(nonatomic, assign)float price;
@end
#import "PlayItem.h"
@implementation PlayItem
@synthesize price = _price;
@synthesize name = _name;
- (void)dealloc{
self.name = nil;
}
//如果设置里面不存在的key就会触发该方法
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"function %@ is calling",NSStringFromSelector(_cmd) );
}
@end
main.m
#import <foundation foundation.h="">
#import "PlayList.h"
#import "PlayItem.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
PlayList *pl = [[PlayList alloc]init];
[pl setValue:@"播放列表" forKey:@"name"];
NSLog(@"name is %@",pl.name);
id v = [pl valueForKey:@"number"];
NSLog(@" v is %@",v);
//设置pl currItem.name 字段
[pl setValue:@"播放列表22" forKeyPath:@"currItem.name"];
NSLog(@"pl.currItem.name is %@",pl.currItem.name);
//设置一批key value
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"200",@"number",@"测试",@"name", nil];
[pl setValuesForKeysWithDictionary:dict];
NSLog(@"name is %@ number is %d",pl.name,pl.number);
//pl对象里没有test这个key,所以系统崩溃
[pl setValue:@"hello" forKey:@"test"];
id obj = [pl valueForKey:@"itemList"];
NSLog(@"obj is %@",obj);
id obj2 = [pl valueForKeyPath:@"itemList.name"];
NSLog(@"obj name is %@",obj2);
// NSLog(@"%@",pl.itemList.) 用点语法无法取到更深的值
//求和,平均值,最大值,最小值
NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@sum.price"]);
NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@avg.price"]);
NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@max.price"]);
NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@min.price"]);
}
return 0;
}
</foundation>
//对比
//如果要想不用kvc的话要取值则要麻烦的多
int sum = 0;
for (PlayItem *item in pl.itemList){
sum += item.price;
}
NSLog(@"num == %d",sum);
//而kvc只需要
NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@sum.price"]);
//如果要想取到PlayList 里的 PlayItem 里的name 不用kvc是报错的,因为点语法无法取到更深的值
// NSLog(@"%@",pl.itemList.) ;
//kvc只需要
id obj3 = [pl valueForKeyPath:@"itemList.name"];
NSLog(@"%@",obj3);
//相比较kvc使代码更加简洁有效 已读
参考文章:设计模式-KVO
如果想了解的更详细,可以参考这两篇文章:
[深入浅出Cocoa]详解键值观察(KVO)及其实现机理 http://blog.youkuaiyun.com/kesalin/article/details/8194240
(译)KVO的内部实现http://www.cocoachina.com/applenews/devnews/2014/0107/7667.html