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的內部實現