KVC/KVO

KVC、KVO即NSKeyValueCoding和NSKeyValueObserving的簡稱。html

 

Key-Value Observing機制服務器

知識點介紹架構

Key-Value Observing (簡寫爲KVO):當指定的對象的屬性被修改了,容許對象接受到通知的機制。每次指定的被觀察對象的屬性被修改的時候,KVO都會自動的去通知相應的觀察者。KVO簡而言之就是:基於鍵值的觀察者,實際上就是觀察者模式。app

 

 

使用方法框架

 

1. 註冊,指定被觀察者的屬性,ide

注意key必定和self.model的title屬性對應,不然會報錯,key不能夠本身隨意填寫函數

2. 實現回調方法ui

3. 移除觀察this


KVO的優勢:
當 有屬性改變,KVO會提供自動的消息通知。這樣的架構有不少好處。首先,開發人員不須要本身去實現這樣的方案:每次屬性改變了就發送消息通知。這是KVO 機制提供的最大的優勢。由於這個方案已經被明肯定義,得到框架級支持,能夠方便地採用。開發人員不須要添加任何代碼,不須要設計本身的觀察者模型,直接可 以在工程裏使用。其次,KVO的架構很是的強大,能夠很容易的支持多個觀察者觀察同一個屬性,以及相關的值。
KVO如何工做:
須要三個步驟來創建一個屬性的觀察員。理解這三個步驟就能夠知道KVO如何設計工做的。 (1)首先,構思一下以下實現KVO是否有必要。好比,一個對象,當另外一個對象的特定屬性改變的時候,須要被通知到。


例 如,PersonObject但願可以覺察到BankObject對象的accountBalance屬性的任何變化。 (2)那麼 PersonObject必須發送一個「addObserver:forKeyPath:options:context:」消息,註冊成爲 BankObject的accountBalance屬性的觀察者。(說 明:「addObserver:forKeyPath:options:context:」方法在指定對象實例之間創建了一個鏈接。注意,這個鏈接不是兩 個類之間創建的,而是兩個對象實例之間創建的。) (3)爲了可以響應消息,觀察者必須實現 「observeValueForKeyPath:ofObject:change:context:」方法。這個方法實現如何響應變化的消息。在這個方 法裏面咱們能夠跟本身的狀況,去實現應對被觀察對象屬性變更的相應邏輯。 (4)假如遵循KVO規則的話,當被觀察的屬性改變的話,方法 「observeValueForKeyPath:ofObject:change:context:」會自動被調用。
參考資料
http://www.cocoadev.cn/CocoaDev/Key-Value-Observing-Quick-Start-cn.asp


本知識點在此例中的應用編碼

 

//註冊監聽@implementation RootViewController
- (void)viewDidLoad 
{
//監聽屬性「earthquakeList」
/* KVO: listen for changes to our earthquake data source for table
view updates*/
[self addObserver:self 
forKeyPath:@"earthquakeList" options:0 context:NULL];
}@end 
//屬性發生改變時
- (void)insertEarthquakes:(NSArray *)earthquakes
{
// this will allow us as an observer to notified 
(see observeValueForKeyPath)*/
// so we can update our UITableView  
[self willChangeValueForKey:@"earthquakeList"];
[self.earthquakeList addObjectsFromArray:earthquakes];
[self didChangeValueForKey:@"earthquakeList"];
}
//當屬性的值發生變化時,自動調用此方法
/* listen for changes to the earthquake list coming from our app delegate. */
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{    
    [self.tableView reloadData];
}

 

 

 

cocoa的KVO模型中,有兩種通知觀察者的方式,自動通知和手動通知。顧名思義,自動通知由cocoa在屬性值變化時自動通知觀察者,而手動通知須要在值變化時調用 willChangeValueForKey:和didChangeValueForKey: 方法通知調用者。爲求簡便,咱們通常使用自動通知。

要使用手動通知,須要在 automaticallyNotifiesObserversForKey方法中明確告訴cocoa,哪些鍵值要使用自動通知:

 

//從新實現NSObject類中的automaticallyNotifiesObserversForKey:方法,返回yes表示自動通知。  
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key  
{  
    //當這兩個值改變時,使用自動通知已註冊過的觀察者,觀察者須要實現observeValueForKeyPath:ofObject:change:context:方法  
    if ([key isEqualToString:@"isFinished"])  
    {  
        return NO;  
    }  
    return [super automaticallyNotifiesObserversForKey:key];  
}

 

手動通知在須要改變值_isFinished變量的地方,使用

[self willChangeValueForKey:@"isFinished"]; 
finished = YES;
[self didChangeValueForKey:@"isFinished"];


自動通知在須要改變_isFinished變量的地方,使用

[self setValue:[NSNumber numberWithBool:YES] forKey:@"isFinished"];

 

方法,而不是僅僅使用簡單賦值。

咱們須要在3個地方改變isFinished值爲YES,請求結束時、鏈接出錯誤,線程被cancel。請在對應的方法代碼中加入上面的語句。

最後,須要在觀察者的代碼中進行註冊。打開ViewController中調用NSOperation子類的地方,加入:

 

 

//kvo註冊  
    [operation addObserver:self forKeyPath:@"isFinished"  
                   options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:operation];  
並實現 observeValueForKeyPath 方法:  
//接收變動通知  
- (void)observeValueForKeyPath:(NSString *)keyPath  
                      ofObject:(id)object  
                       change:(NSDictionary *)change  
                       context:(void *)context  
{  
    if ([keyPath isEqual:@"isFinished"]) {  
        BOOL isFinished=[[change objectForKey:NSKeyValueChangeNewKey] intValue];  
        if (isFinished) {//若是服務器數據接收完畢  
            [indicatorView stopAnimating];  
            URLOperation* ctx=(URLOperation*)context;  
            NSStringEncoding enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);  
            NSLog(@"%@",[[NSString alloc] initWithData:[ctx data] encoding:enc]);  
            //取消kvo註冊  
            [ctx removeObserver:self  
                    forKeyPath:@"isFinished"];  
        }        
    }else{  
        // be sure to call the super implementation  
        // if the superclass implements it  
        [super observeValueForKeyPath:keyPath  
                             ofObject:object  
                               change:change  
                              context:context];  
    }  
}

 

 

 

實例:

假設一個場景,股票的價格顯示在當前屏幕上,當股票價格更改的時候,實時顯示更新其價格。

1.定義DataModel,

 

@interface StockData : NSObject {  
    NSString * stockName;  
    float price;  
}  
@end  
@implementation StockData  
@end

 

 

2.定義此model爲Controller的屬性,實例化它,監聽它的屬性,並顯示在當前的View裏邊

 

 

- (void)viewDidLoad  
{  
    [super viewDidLoad];  
  
    stockForKVO = [[StockData alloc] init];  
    [stockForKVO setValue:@"searph" forKey:@"stockName"];  
    [stockForKVO setValue:@"10.0" forKey:@"price"];      
    [stockForKVO addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];  
  
    myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 30 )];  
    myLabel.textColor = [UIColor redColor];  
    myLabel.text = [stockForKVO valueForKey:@"price"];  
    [self.view addSubview:myLabel];  
     
    UIButton * b = [UIButton buttonWithType:UIButtonTypeRoundedRect];  
    b.frame = CGRectMake(0, 0, 100, 30);  
    [b addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];  
    [self.view addSubview:b];  
  
}

 


 

3.當點擊button的時候,調用buttonAction方法,修改對象的屬性

 

-(void) buttonAction  
{  
    [stockForKVO setValue:@"20.0" forKey:@"price"];  
}

 

4. 實現回調方法

 

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context  
{  
    if([keyPath isEqualToString:@"price"])  
    {  
        myLabel.text = [stockForKVO valueForKey:@"price"];  
    }  
}

 

5.增長觀察與取消觀察是成對出現的,因此須要在最後的時候,移除觀察者

 

- (void)dealloc  
{  
    [super dealloc];  
    [stockForKVO removeObserver:self forKeyPath:@"price"];  
    [stockForKVO release];  
}

 

小結

KVO這種編碼方式使用起來很簡單,很適用與datamodel修改後,引起的UIVIew的變化這種狀況,就像上邊的例子那樣,當更改屬性的值後,監聽對象會當即獲得通知。

 

 

Demo源代碼:

 

KVC

 

isa-swizzling就是類型混合指針機制。KVC主要經過isa-swizzling,來實現其內部查找定位的。


isa指針,就是is a kind of的意思,指向維護分發表的對象的類。該分發表實際上包含了指向實現類中的方法的指針,和其它數據。

以下KVC的代碼:

[person setValue:@"personName" forKey:@"name"];

就會被編譯器處理成:
 

SEL sel = sel_get_uid ("setValue:forKey:");

IMP method = objc_msg_lookup (person->isa,sel);

method(person, sel, @"personName", @"name");

 


其中:

SEL數據類型:它是編譯器運行Objective-C裏的方法的環境參數。

IMP數據類型:他其實就是一個 編譯器內部實現時候的函數指針。當Objective-C編譯器去處理實現一個方法的時候,就會指向一個IMP對象,這個對象是C語言表述的類型。

***

KVC在調用方法setValue的時候

(1)首先根據方法名找到運行方法的時候所須要的環境參數。

(2)他會從本身isa指針結合環境參數,找到具體的方法實現的接口。

(3)再直接查找得來的具體的方法實現。

這樣的話前面介紹的KVO實現就好理解了

當一個對象註冊了一個觀察者,被觀察對象的isa指針被修改的時候,isa指針就會指向一箇中間類,而不是真實的類。

因此isa指針其實不須要指向實例對象真實的類。因此咱們的程序最好不要依賴於isa指針。在調用類的方法的時候,最好要明確對象實例的類名。
 

這樣只有當咱們調用KVC去訪問key值的時候KVO纔會起做用。因此確定肯定的是,KVO是基於KVC實現的。

本站公眾號
   歡迎關注本站公眾號,獲取更多信息