【OC基礎】04-OC中的KVC、KVO

概述

在Java和C#中都提供了反射的功能,既根據字符串能動態的建立對象並修改對象的屬性。OC內置了這些功能,使得咱們在操做的時候更方便。編程

鍵值編碼KVC

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

  • 若是是動態設置屬性,則優先考慮調用setA方法,若是沒有該方法則優先考慮搜索成員變量_a,若是仍然不存在則搜索成員變量a,若是最後仍然沒搜索到則會調用這個類的setValue:forUndefinedKey:方法(注意搜索過程當中無論這些方法、成員變量是私有的仍是公共的都能正確設置);
  • 若是是動態讀取屬性,則優先考慮調用a方法(屬性a的get方法),若是沒有搜索到則會優先搜索成員變量_a,若是仍然不存在則搜索成員變量a,若是最後仍然沒搜索到則會調用這個類的valueforUndefinedKey:方法(注意搜索過程當中無論這些方法、成員變量是私有的仍是公共的都能正確讀取);

鍵值監聽KVO

在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: forKeyPathremoveObserver: 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>重寫這個回調函數

這樣在改變這個屬性的時候就會自動調用咱們的回調函數,實現了觀察者模式。

相關文章
相關標籤/搜索