在Java和C#中都提供了反射的功能,既根據字符串能動態的建立對象並修改對象的屬性。OC內置了這些功能,使得咱們在操做的時候更方便。編程
kvc是Key for Value的縮寫,KVC的操做方法由NSKeyValueCoding協議提供,而NSObject就實現了這個協議,也就是說ObjC中幾乎全部的對象都支持KVC操做,經常使用的KVC操做方法以下:函數
1>動態設置: setValue:屬性值 forKey:屬性名(用於簡單路徑)、setValue:屬性值 forKeyPath:屬性路徑(用於複合路徑,例如Person有一個Account類型的屬性,那麼person.account就是一個複合屬性)編碼
2>動態讀取: valueForKey:屬性名 、valueForKeyPath:屬性名(用於複合路徑)atom
例子以下:spa
Book.hcode
1 #import <Foundation/Foundation.h> 2 3 @interface Book : NSObject 4 @property (nonatomic,copy) NSString* bookName; 5 @property (nonatomic,assign)float price; 6 @end
Book.mserver
1 #import "Book.h" 2 3 @implementation Book 4 5 @end
Student.h對象
1 #import <Foundation/Foundation.h> 2 #import "Book.h" 3 @interface Student : NSObject 4 5 #pragma mark - 屬性 6 @property (nonatomic,assign)int age; 7 @property (nonatomic,copy) NSString *name; 8 @property (nonatomic,retain) Book *book; 9 -(void)show; 10 @end
Student.mblog
1 #import "Student.h" 2 3 @implementation Student 4 -(void)show{ 5 NSLog(@"name=%@ and age=%d",_name,_age); 6 } 7 @end
main.mrem
1 #import <Foundation/Foundation.h> 2 #import "Student.h" 3 int main(int argc, const char * argv[]) 4 { 5 6 @autoreleasepool { 7 Student *stu1=[[Student alloc]init]; 8 [stu1 setValue:@"lisi" forKey:@"name"]; 9 [stu1 setValue:@14 forKey:@"age"]; 10 [stu1 show]; 11 12 NSLog(@"stu1 name=%@,age=%@",[stu1 valueForKey:@"name"],[stu1 valueForKey:@"age"]); 13 14 Book *book1=[[Book alloc]init]; 15 stu1.book=book1; 16 [stu1 setValue:@"99.8" forKeyPath:@"book.price"]; 17 [stu1 setValue:@"mathBook" forKeyPath:@"book.bookName"]; 18 NSLog(@"stu1 bookname=%@ price=%@",[stu1 valueForKeyPath:@"book.bookName"],[stu1 valueForKeyPath:@"book.price"]); 19 20 } 21 return 0; 22 }
KVC是如何對屬性進行讀取的呢?假如要讀取屬性a
在C#或者Java圖形界面編程的時候,咱們改變控件的某個屬性,通常都能動態的反應到UI的變化上。這實際上利用的觀察者模式,利用這種模式很容易實現數據模型和界面操做的分離。OC默認內置了這項功能叫KVO。在OC中要實現KVO則必須實現NSKeyValueObServing協議,不過NSObject已經實現了該協議,所以幾乎全部的ObjC對象均可以使用KVO。
在OC中使用KVO操做經常使用的方法以下:
1>註冊監聽: addObserver: forKeyPath: options: context:
2>回調監聽: observeValueForKeyPath: ofObject: change: context:
3>刪除監聽: removeObserver: forKeyPath、removeObserver: forKeyPath: context:
KVO的使用步驟也比較簡單:
1>經過addObserver: forKeyPath: options: context:爲被監聽對象(它一般是數據模型)註冊監聽器
2>重寫監聽器的observeValueForKeyPath: ofObject: change: context:方法
上面的例子中,假如咱們但願在Book價格改變的時候通知Student,咱們能夠這樣寫:
Student.h:
1 #import <Foundation/Foundation.h> 2 #import "Book.h" 3 @interface Student : NSObject 4 5 #pragma mark - 屬性 6 @property (nonatomic,assign)int age; 7 @property (nonatomic,copy) NSString *name; 8 @property (nonatomic,retain) Book *book; 9 -(void)show; 10 @end
Student.m:
1 #import "Student.h" 2 3 @implementation Student 4 -(void)show{ 5 NSLog(@"name=%@ and age=%d",_name,_age); 6 } 7 8 -(void)setBook:(Book *)book{ 9 _book=book; 10 //添加對Book的監聽 11 [self.book addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew context:nil]; 12 } 13 14 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ 15 //Book價格變化會調用這個函數 16 if([keyPath isEqualToString:@"price"]){ 17 NSLog(@"keyPath=%@,Obj=%@,newValue=%f,context=%@",keyPath,object,[[change objectForKey:@"new"]floatValue],context); 18 } 19 } 20 -(void)dealloc{ 21 //移除監聽 22 [self.book removeObserver:self forKeyPath:@"price"]; 23 [super dealloc]; 24 } 25 @end
main.m:
1 #import <Foundation/Foundation.h> 2 #import "Student.h" 3 int main(int argc, const char * argv[]) 4 { 5 6 @autoreleasepool { 7 Student *stu1=[[Student alloc]init]; 8 [stu1 setValue:@"lisi" forKey:@"name"]; 9 [stu1 setValue:@14 forKey:@"age"]; 10 [stu1 show]; 11 12 NSLog(@"stu1 name=%@,age=%@",[stu1 valueForKey:@"name"],[stu1 valueForKey:@"age"]); 13 14 Book *book1=[[Book alloc]init]; 15 stu1.book=book1; 16 [stu1 setValue:@"99.8" forKeyPath:@"book.price"]; 17 [stu1 setValue:@"mathBook" forKeyPath:@"book.bookName"]; 18 NSLog(@"stu1 bookname=%@ price=%@",[stu1 valueForKeyPath:@"book.bookName"],[stu1 valueForKeyPath:@"book.price"]); 19 20 } 21 return 0; 22 }
結果輸出:
1 2015-05-04 11:54:54.557 first[2697:303] name=lisi and age=14 2 2015-05-04 11:54:54.559 first[2697:303] stu1 name=lisi,age=14 3 2015-05-04 11:54:54.560 first[2697:303] keyPath=price,Obj=<Book: 0x1024004a0>,newValue=99.800003,context=(null) 4 2015-05-04 11:54:54.560 first[2697:303] stu1 bookname=mathBook price=99.8
咱們給Book的price屬性註冊了監聽,並添加了監聽回調方法,這樣在咱們改變Book的price屬性時,就會調用咱們的回調方法打印出相關的信息。經過這個例子咱們能夠看出,KVO的本質是:
1>在要監聽的成員中的get/set方法中註冊回調函數
2>重寫這個回調函數
這樣在改變這個屬性的時候就會自動調用咱們的回調函數,實現了觀察者模式。