iOS中KVO,KVC的學習記錄
KVO
#import#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@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
PlayList
#import#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
@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
#import#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的內部實現