(转载)OC学习篇之---KVC和KVO操作
前一篇文章我們介紹了OC中最常用的文件操作,那么今天來看一下OC中的一個比較有特色的知識點:KVC和KVO
?
一、KVC操作
OC中的KVC操作就和Java中使用反射機制去訪問類的private權限的變量,很暴力的,這樣做就會破壞類的封裝性,本來類中的的private權限就是不希望外界去訪問的,但是我們這樣去操作,就會反其道而行,但是我們有時候真的需要去這樣做,哎。所以說有些事不是都是順其自然的,而是需要的時候自然就誕生了。
下面就來看一下這種技術的使用:
Dog.h
1 // 2 // Dog.h 3 // 42_KVC 4 // 5 // Created by jiangwei on 14-10-14. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface Dog : NSObject 12 13 @endDog.m
1 // 2 // Dog.m 3 // 42_KVC 4 // 5 // Created by jiangwei on 14-10-14. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import "Dog.h" 10 11 @implementation Dog 12 13 @end定義了Dog這個類,但是什么都沒有,他只是一個中間類,沒什么作用,在這個demo中。
Person.h
1 // 2 // Person.h 3 // 42_KVC 4 // 5 // Created by jiangwei on 14-10-14. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Dog.h" 11 12 @interface Person : NSObject{ 13 @private 14 NSString *_name; 15 NSDog *_dog; 16 17 NSInteger *age; 18 } 19 20 @endPerson.m
1 // 2 // Person.m 3 // 42_KVC 4 // 5 // Created by jiangwei on 14-10-14. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import "Person.h" 10 11 @implementation Person 12 13 - (NSString *)description{ 14 NSLog(@"%@",_name); 15 return _name; 16 } 17 18 @endPerson類中我們定義了兩個屬性,但是這兩個屬性對外是不可訪問的,而且也沒有對應的get/set方法。我們也實現了description方法,用于打印結果。
看一下測試代碼:
main.m
1 // 2 // main.m 3 // 42_KVC 4 // 5 // Created by jiangwei on 14-10-14. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Person.h" 11 #import "Dog.h" 12 13 //KVC:很暴力,及時一個類的屬性是私有的,而且也沒有get/set方法,同樣可以讀寫 14 //相當于Java中的反射,破壞類的封裝性 15 int main(int argc, const charchar * argv[]) { 16 @autoreleasepool { 17 18 Person *p = [[Person alloc] init]; 19 20 //設置值 21 //這里setValue方法:第一個參數是value,第二個參數是key(就是類的屬性名稱) 22 [p setValue:@"jiangwei" forKey:@"name"]; 23 24 Dog *dog = [[Dog alloc] init]; 25 [p setValue:dog forKey:@"dog"]; 26 27 //KVC設置值時,如果屬性有set方法,則優先調用set方法,如果沒有則直接設置上去,get方法類似 28 29 //讀取值 30 NSString *name = [p valueForKey:@"name"]; 31 32 //設置基本數據類型 33 //這里需要將基本類型轉化成NSNumber 34 //在設置值的時候,會有自動解包的過程,NSNumber會解包賦值給age 35 [p setValue:@22 forKey:@"age"]; 36 37 NSLog(@"%@",p); 38 39 return 0; 40 } 41 return 0; 42 }這里我們生成一個Person對象,然后開始使用KVC技術了:
1、設置屬性值
1 //設置值 2 //這里setValue方法:第一個參數是value,第二個參數是key(就是類的屬性名稱) 3 [p setValue:@"jiangwei" forKey:@"name"]; 4 5 Dog *dog = [[Dog alloc] init]; 6 [p setValue:dog forKey:@"dog"];使用setValue方法,就可以進行對屬性進行設置值操作了,同時需要傳遞這個屬性的名稱,這個和Java中使用反射機制真的很像。
注:KVC設置值時,如果屬性有set方法,則優先調用set方法,如果沒有則直接設置上去,get方法一樣
1 //設置基本數據類型 2 //這里需要將基本類型轉化成NSNumber 3 //在設置值的時候,會有自動解包的過程,NSNumber會解包賦值給age 4 [p setValue:@22 forKey:@"age"];還有一個需要注意的地方:當我們在設置基本類型的時候,需要將其轉化成NSNumber類型的。
2、取屬性的值
1 //讀取值 2 NSString *name = [p valueForKey:@"name"];取值就簡單了。
?
下面再來看一下KVC中強大的功能:鍵值路徑
鍵值路徑是對于一個類中有數組對象的屬性進行便捷操作。
看個場景:
一個作者有多本書
Author.h
1 // 2 // Author.h 3 // 43_KeyValuePath 4 // 5 // Created by jiangwei on 14-10-15. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface Author : NSObject{ 12 NSString *_name; 13 14 //作者出版的書,一個作者對應多個書籍對象 15 NSArray *_issueBook; 16 } 17 18 @end作者類中定義了名字和一個書籍數組
Author.m
1 // 2 // Author.m 3 // 43_KeyValuePath 4 // 5 // Created by jiangwei on 14-10-15. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import "Author.h" 10 11 @implementation Author 12 13 @endBook.h
1 // 2 // Book.h 3 // 43_KeyValuePath 4 // 5 // Created by jiangwei on 14-10-15. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Author.h" 11 12 @interface Book : NSObject{ 13 Author *_author; 14 } 15 16 @property NSString *name; 17 @property floatfloat *price; 18 19 @end定義了一個作者屬性,書的名字,價格
Book.m
1 // 2 // Book.m 3 // 43_KeyValuePath 4 // 5 // Created by jiangwei on 14-10-15. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import "Book.h" 10 11 @implementation Book 12 13 @end看一下測試代碼:
main.m
1 // 2 // main.m 3 // 43_KeyValuePath 4 // 5 // Created by jiangwei on 14-10-15. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Book.h" 11 #import "Author.h" 12 13 int main(int argc, const charchar * argv[]) { 14 @autoreleasepool { 15 16 //------------------KVC鍵值路徑 17 /* 18 Book *book = [[Book alloc] init]; 19 Author *author = [[Author alloc] init]; 20 21 //設置作者 22 [book setValue:author forKey:@"author"]; 23 24 //設置作者的名字 25 //路徑為:author.name,中間用點號進行連接 26 [book setValue:@"jiangwei" forKeyPath:@"author.name"]; 27 NSString *name = [author valueForKey:@"name"]; 28 NSLog(@"name is %@",name); 29 */ 30 31 32 //--------------------KVC的運算 33 Author *author = [[Author alloc] init]; 34 [author setValue:@"莫言" forKeyPath:@"name"]; 35 36 Book *book1 = [[Book alloc] init]; 37 book1.name = @"紅高粱"; 38 book1.price = 9; 39 Book *book2 = [[Book alloc] init]; 40 book2.name = @"蛙"; 41 book2.price = 10; 42 NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil]; 43 [author setValue:array forKeyPath:@"issueBook"]; 44 45 //基本數據類型會自動被包裝成NSNumber,裝到數組中 46 //得到所有書籍的價格 47 NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"]; 48 NSLog(@"%@",priceArray); 49 50 //獲取數組的大小 51 NSNumber *count = [author valueForKeyPath:@"issueBook.@count"]; 52 NSLog(@"count=%@",count); 53 54 //獲取書籍價格的總和 55 NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"]; 56 NSLog(@"%@",sum); 57 58 //獲取書籍的平均值 59 NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"]; 60 NSLog(@"%@",avg); 61 62 //獲取書籍的價格最大值和最小值 63 NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"]; 64 NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"]; 65 66 } 67 return 0; 68 }1、首先通過前面說到的KVC設置作者的書籍數組
1 //--------------------KVC的運算 2 Author *author = [[Author alloc] init]; 3 [author setValue:@"莫言" forKeyPath:@"name"]; 4 5 Book *book1 = [[Book alloc] init]; 6 book1.name = @"紅高粱"; 7 book1.price = 9; 8 Book *book2 = [[Book alloc] init]; 9 book2.name = @"蛙"; 10 book2.price = 10; 11 NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil]; 12 [author setValue:array forKeyPath:@"issueBook"];添加了兩本書籍。
2、下面就開始用到KVC中鍵值路徑了
1)獲取作者類中書籍數組中所有書籍的價格
1 //基本數據類型會自動被包裝成NSNumber,裝到數組中 2 //得到所有書籍的價格 3 NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"]; 4 NSLog(@"%@",priceArray);看到了:@"issueBook.price" 這就是鍵值路徑的使用,issueBook是作者類中的書籍數組屬性名,price是書籍類的屬性,中間用點號進行連接,這樣我們就可以獲取到了所有書籍的價格了,如果在Java中,我們需要用一個循環操作。但是OC中多么方便。
2)獲取作者類中書籍數組的大小
1 //獲取數組的大小 2 NSNumber *count = [author valueForKeyPath:@"issueBook.@count"]; 3 NSLog(@"count=%@",count);使用 @"issueBook.@count" 鍵值路徑獲取書籍數組的大小,issueBook是作者類中的書籍數組屬性名,@count是特定一個寫法,可以把它想象成一個方法,中間任然用點號進行連接。
3)獲取作者類中書籍數組的價格總和
1 //獲取書籍價格的總和 2 NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"]; 3 NSLog(@"%@",sum);使用 @"issueBook.@sum.price" 鍵值路徑獲取書籍數組中的價格總和,issueBook是作者類中的書籍數組屬性名,@sum是特性寫法,可以把它想象成一個方法,price是書籍的價格屬性名,可以把它看成是@sum的一個參數,中間用點號進行連接。
如果在java中,這個需要用一個循環來計算總和,OC中很方便的。
4)獲取作者類中書籍數組的價格平均值、最小值、最大值
1 //獲取書籍的平均值 2 NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"]; 3 NSLog(@"%@",avg); 4 5 //獲取書籍的價格最大值和最小值 6 NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"]; 7 NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];操作和上面類似,這里就不解釋了。
我們看到上面返回來的數據都是NSNumber類型的。
二、KVO操作
KVO操作在OC中也是經常會用到的,而且這種機制在java中不存在的。
它的作用就是用來監聽類中屬性值的變化,實現原理是觀察者模式,當然我們也可以使用觀察者模式在Java中實現這樣的機制。
看一下具體的例子:現在有一個小孩類,他有兩個屬性:開心值,饑餓值,然后還有一個護士類,用來監聽孩子類的這兩個屬性值的。
Chidren.h
1 // 2 // Children.h 3 // 44_KVO 4 // 5 // Created by jiangwei on 14-10-16. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface Children : NSObject 12 13 @property NSInteger *hapyValue; 14 @property NSInteger *hurryValue; 15 16 17 @endChildren.m
1 // 2 // Children.m 3 // 44_KVO 4 // 5 // Created by jiangwei on 14-10-16. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import "Children.h" 10 11 @implementation Children 12 13 - (id) init{ 14 self = [super init]; 15 if(self != nil){ 16 //啟動定時器 17 [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES]; 18 self.hapyValue= 100; 19 } 20 return self; 21 } 22 23 - (void) timerAction:(NSTimer *) timer{ 24 //使用set方法修改屬性值,才能觸發KVO 25 26 int value = _hapyValue; 27 [self setHapyValue:--value]; 28 29 int values = _hurryValue; 30 [self setHurryValue:--values]; 31 } 32 33 34 @end在初始化方法中,我們啟動一個定時器,然后隔1s就去修改孩子類的值?
Nure.h
1 // 2 // Nure.h 3 // 44_KVO 4 // 5 // Created by jiangwei on 14-10-16. 6 // Copyright (c) 2014年 jiangwei. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @class Children; 12 @interface Nure : NSObject{ 13 Children *_children; 14 } 15 16 - (id) initWithChildren:(Children *)children; 17 18 @end 定義一個孩子屬性
Nure.m
看到了在這里就開始進行監聽操作了。
下面來具體看一下如何做到監聽的:
1、添加監聽對象
我們使用addObserver方法給孩子添加監聽對象。
第一個參數:監聽者,這里是Nure,所以可以直接傳遞self
第二個參數:監聽對象的屬性名
第三個參數:監聽這個屬性的狀態:這里可以使用|進行多種組合操作,屬性的新值和舊值
第四個參數:傳遞內容給監聽方法
1 //觀察小孩的hapyValue 2 //使用KVO為_children對象添加一個觀察者,用于觀察監聽hapyValue屬性值是否被修改 3 [_children addObserver:self forKeyPath:@"hapyValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"]; 4 5 //觀察小孩的hurryValue 6 [_children addObserver:self forKeyPath:@"hurryValue" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:@"context"];2、監聽方法
1 //觸發方法 2 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context{ 3 NSLog(@"%@",change); 4 //通過打印change,我們可以看到對應的key 5 6 //通過keyPath來判斷不同屬性的觀察者 7 if([keyPath isEqualToString:@"hapyValue"]){ 8 //這里change中有old和new的值是因為我們在調用addObserver方法時,用到了 9 //NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一個就用哪一個 10 //[change objectForKey:@"old"]是修改前的值 11 NSNumber *hapyValue = [change objectForKey:@"new"];//修改之后的最新值 12 13 NSInteger *value = [hapyValue integerValue]; 14 15 if(value < 90){ 16 //do something... 17 } 18 }else if([keyPath isEqualToString:@"hurryValue"]){ 19 //這里change中有old和new的值是因為我們在調用addObserver方法時,用到了 20 //NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;想要哪一個就用哪一個 21 //[change objectForKey:@"old"]是修改前的值 22 NSNumber *hurryValue = [change objectForKey:@"new"];//修改之后的最新值 23 24 NSInteger *value = [hurryValue integerValue]; 25 26 if(value < 90){ 27 //do something... 28 } 29 } 30 31 NSLog(@"%@",context);//打印的就是addObserver方法的context參數 32 33 34 35 //使用KVC去修改屬性的值,也會觸發事件 36 }我們上面傳遞的第一個參數是監聽者,這個方法也是在監聽者中實現的,當屬性值發生變化的時候,這個方法會被回調。
這個方法的參數:
第一個參數:鍵值路徑
第二個參數:監聽對象
第三個參數:變化的值
第四個參數:傳遞的內容
我們看到代碼中有一個特殊的參數:第三個參數:NSDirctionary類型的。其實我們如果不知道是干什么的,我們可以打印一下他的結果看一下,很簡單,這里就不截圖說明了,我們會發現他有兩個鍵值:key是new和old。他們就是分別代表這個屬性值變化的前后值,同時他們的得到也和之前我們添加監聽對象時設置的第三個參數有關:
NSKeyValueObservingOptionNew?|NSKeyValueObservingOptionOld
那個地方設置了幾種狀態,這里的NSDirctionary中就會有幾個鍵值對。
3、銷毀方法
這個并不屬于KVO的內容了,只是在這里用到了就順便說一下
1 - (void)dealloc{ 2 3 //移除觀察者 4 [_children removeObserver:self forKeyPath:@"hapyValue"]; 5 [_children removeObserver:self forKeyPath:@"hurryValue"]; 6 7 }我們在創建一個對象的時候會調用alloc方法,當對象被銷毀的時候會調用dealloc這個方法,這個和C++中的析構函數一樣,Java中有垃圾回收器,所以沒有此類的方法,但是有一個finalize方法,其實這個方法就是在垃圾回收器回收對象的時候會調用,和這個功能差不多,但是在Java中,我們并不提倡使用這個方法。因為會造成GC的回收發生錯誤。
我們在銷毀方法中需要移除監聽者。
?
總結
這一篇就介紹了OC中比較有特色的兩個機制:KVC和KVO
KVC:就是可以暴力的去get/set類的私有屬性,同時還有強大的鍵值路徑對數組類型的屬性進行操作
KVO:監聽類中屬性值變化的
轉載于:https://www.cnblogs.com/iLillian/p/4199475.html
總結
以上是生活随笔為你收集整理的(转载)OC学习篇之---KVC和KVO操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 轻量级web富文本框——wangEdit
- 下一篇: unity值得推荐的网址