iOS中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
#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
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
#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; }
自己的理解
//对比 //如果要想不用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)及其实现机理 (译)KVO的内部实现