iOS KVO & KVC

鍵值觀察:值更改時通知觀察者

鍵值觀察(Key-value observing,或簡稱 KVO)容許對象觀察另外一個對象的屬性。該屬性值改變時,會通知觀察對象。它瞭解新值以及舊值;若是觀察的屬性爲對多的關係(例如數組),它也要了解哪一個包含的對象發生了改變。KVO 有助於使應用程序變得更內聚,保持模型、控制器和視圖層中的對象與改變同步。html


與 NSNotificationCenter 通知類似,多個 KVO 觀察者能夠觀察單一屬性。此外,KVO 更動態,由於它容許對象觀察任意屬性,而不需任何新的 API,例如通知名稱。KVO 是一個輕量級點對點通訊機制,不容許觀察全部實例的特定屬性。數組

----以上解釋來着官方文檔----架構

 

Key-Value Observing機制的概述app

Key-Value Observing (簡寫爲KVO):當指定的對象的屬性被修改了,容許對象接受到通知的機制。每次指定的被觀察對象的屬性被修改的時候,KVO都會自動的去通知相應的觀察者。框架

KVO的優勢函數

當有屬性改變,KVO會提供自動的消息通知。這樣的架構有不少好處。首先,開發人員不須要本身去實現這樣的方案:每次屬性改變了就發送消息通知。這是KVO機制提供的最大的優勢。由於這個方案已經被明肯定義,得到框架級支持,能夠方便地採用。開發人員不須要添加任何代碼,不須要設計本身的觀察者模型,直接能夠在工程裏使用。其次,KVO的架構很是的強大,能夠很容易的支持多個觀察者觀察同一個屬性,以及相關的值。測試

KVO如何工做atom

須要三個步驟來創建一個屬性的觀察員。理解這三個步驟就能夠知道KVO如何設計工做的。spa

 

(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:」會自動被調用。




 

KVO是Cocoa的一個重要機制,他提供了觀察某一屬性變化的方法,極大的簡化了代碼。這種觀察-被觀察模型適用於這樣的狀況,比方說根據A(數據類)的某個屬性值變化,B(view類)中的某個屬性作出相應變化。對於推崇MVC的cocoa而言,kvo應用的地方很是普遍。

適用kvo時,一般遵循以下流程:

1 註冊:

-(void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void*)context

keyPath就是要觀察的屬性值,options給你觀察鍵值變化的選擇,而context方便傳輸你須要的數據(注意這是一個void型)

2 實現變化方法:

-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void*)context

change裏存儲了一些變化的數據,好比變化前的數據,變化後的數據;若是註冊時context不爲空,這裏context就能接收到。

是否是很簡單?kvo的邏輯很是清晰,實現步驟簡單。

說了這麼多,你們都要躍躍欲試了吧。但是,在此以前,咱們還須要瞭解KVC機制。其實,知道了kvo的邏輯只是幫助你理解而已,要真正掌握的,不在於kvo的實現步驟是什麼,而在於KVC,由於只有符合KVC標準的對象才能使用kvo(強烈推薦要使用kvo的人先理解KVC)。


KVC--Key - value coding 機制

 

KVC是一種間接訪問對象屬性(用字符串表徵)的機制,而不是直接調用對象的accessor方法或是直接訪問成員對象。

key就是肯定對象某個值的字符串,它一般和accessor方法或是變量同名,而且必須以小寫字母開頭。

Key path就是以「.」分隔的key,property can be object whick also contains property。好比咱們能夠person這樣的key,也能夠有key.gender這樣的key path。

(setValue:forKey,valueForKey:)、(setValue:forKeyPath,valueForKeyPath:

獲取屬性值時能夠經過valueForKey:的方法,設置屬性值用setValue:forKey:。與此同時,KVC還對未定義的屬性值定義了 valueForUndefinedKey:,你能夠重載以獲取你要的實現(補充下,KVC定義載NSKeyValueCoding的非正式協議裏)。

 

在O-C 2.0引入了property,咱們也能夠經過 .(點)運算符來訪問屬性。下面直接看個例子:

@property NSInteger number;

instance.number 
= 3 ;
[instance setValue:[NSNumber numberWithInteger:
3 ] forKey: @" number " ];

注意KVC中的value都必須是對象。

下面舉個例子解釋一下KVC:

1.先定義兩數據模型,下面的這兩個model是沒法直接訪問屬性的。

 

@interface BookData : NSObject {
    NSString * bookName;
    float price;
    AuthorData * author;
}
@end
@implementation BookData
@end

 

@interface AuthorData : NSObject {
    NSString * name;
}
@end
@implementation AuthorData
@end


使用KVC

 

 

AuthorData * author1 = [[AuthorData alloc] init];
[author1 setValue:@"tom" forKey:@"name"];
BookData * book1 = [[BookData alloc] init];
[book1 setValue:@"english" forKey:@"bookName"];
[book1 setValue:@"20.0" forKey:@"price"];
[book1 setValue:author1 forKey:@"author"];

NSLog(@"value=%@",[book1 valueForKey:@"bookName"]);
NSLog(@"price=%f",[[book1 valueForKey:@"price"] floatValue]);
NSLog(@"author=%@",[book1 valueForKeyPath:@"author.name"]);


下面舉一個例子解釋一下 KVO

 

Person.h

 

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(nonatomic,retain)NSString *name;
@property(nonatomic,assign)NSInteger age;

-(void)changeName;

@end

 

Person.m

 

#import "Person.h"

@implementation Person

@synthesize name,age;//屬性name將被監視

-(void)changeName
{
    name=@"changeName directly";
}

@end

 

PersonMonitor.h

 

#import <Foundation/Foundation.h>

@interface PersonMonitor : NSObject

@end

 

PersonMonitor.m

 

#import "PersonMonitor.h"

@implementation PersonMonitor


//觀察者須要實現的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if([keyPath isEqual:@"name"])
    {
        NSLog(@"change happen,old:%@ ,new:%@;context = %@",[change objectForKey:NSKeyValueChangeOldKey],[change objectForKey:NSKeyValueChangeNewKey],context);
    }
}

@end

 

main.m

 

#import <Foundation/Foundation.h>
#import "Person.h"
#import "PersonMonitor.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        //測試代碼
        //監視對象
        Person *p =[[Person alloc] init];

        //觀察者對象
        PersonMonitor *pm= [[PersonMonitor alloc]init];
        
        //Observer KVO收到通知指定的關鍵路徑相對於接收器。
        /*
         *pm 對象註冊KVO通知。觀察者必須實現鍵值觀察方法observeValueForKeyPath。
         *forKeyPath 關鍵路徑,相對於接收器,用於觀察的屬性。這個值不能爲零。
         *options NSKeyValueObservingOptions的組合值,指定包含在觀察通知的內容。
         *context 任意的數據傳遞給anObserver在observeValueForKeyPath。
         */
        
        [p addObserver:pm forKeyPath:@"name" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:@"hello world"];
        
        //測試前的數據
        NSLog(@"p.name is %@",p.name);
        
        //經過setvalue 的方法,PersonMonitor的監視將被調用
        [p setValue:@"name kvc" forKey:@"name"];
        
        //查看設置後的值
        NSLog(@"p.name is %@",[p valueForKey:@"name"]);
        
        //效果和經過setValue 是一致的
        p.name=@"name change by .name";
        
        //經過person本身的函數來更改 name
        [p changeName]; 
        //最後一次修改是直接修改,因此無法產生通知。只有當咱們調用KVC去訪問key值的時候KVO纔會起做用
        NSLog(@"p.name is %@",p.name);

        //刪除觀察者
//        [p removeObserver:pm forKeyPath:@"name"];
        
        }
    return 0;
}


終端輸出結果:

 

 

2013-07-19 11:04:17.946 KVC_KVOTest[1187:303] p.name is (null)

2013-07-19 11:04:17.948 KVC_KVOTest[1187:303] change happen,old:<null> ,new:name kvc;context = hello world

2013-07-19 11:04:17.949 KVC_KVOTest[1187:303] p.name is name kvc

2013-07-19 11:04:17.949 KVC_KVOTest[1187:303] change happen,old:name kvc ,new:name change by .name;context = hello world

2013-07-19 11:04:17.950 KVC_KVOTest[1187:303] p.name is changeName directly

 

剛接觸KVO  KVC 因此只是懂了個大概,之後仍是要經過實踐應用才更深刻的理解。

註明:以上內容基本上參考了其餘博友的文章和例子,這裏整合了。大笑

相關文章
相關標籤/搜索