1、KVC
KVC是一種間接通路對象屬性(用字元串表征)的機制,而不是直接調用對象的accessor方法或是點(.)直接通路成員對象的屬性。例如通路一個對象屬性我們可以person.age 也可以通過kvc的方式 [personvalueForKey:@"age"]
key就是确定對象某個值的字元串,也就是屬性的名稱,它通常和accessor方法或是變量同名,并且必須以小寫字母開頭。Keypath就是以“.”分隔的key,因為屬性值也能包含屬性。比如我們可以person這樣的key,也可以有key.gender這樣的keypath。
擷取屬性值時可以通過valueForKey:的方法,設定屬性值用setValue:forKey:。與此同時,KVC還對未定義的屬性值定義了valueForUndefinedKey:,你可以重載以擷取你要的實作(補充下,KVC定義載NSKeyValueCoding的非正式協定裡)。注意的是KVC的Value都必須是對象。
在O-C 2.0引入了property,我們也可以通過.運算符來通路屬性。下面直接看個例子:
@property NSIntegernumber;
instance.number = 3 ;
[instance setValue:[NSNumber numberWithInteger: 3 ]forKey: @" number " ];
以上介紹了通過KVC來擷取/設定屬性,接下來要說明下實作KVC的通路器方法(accessormethod)。Apple給出的慣例通常是:
-key:,以及setKey:(使用的nameconvention和setter/getter命名一緻)。對于未定義的屬性可以用setNilValueForKey:。
至此,KVC的基本概念你應該已經掌握了。之是以是基本,因為隻涉及到了單值情況,kvc還可以運用到對多關系,這裡就不說了,留給各位自我學習的空間。
2、KVO
既然我們能用KVC通路屬性,當這個屬性的值發生變化時,我們也能很好的監聽。當然這個過程系統已經給我們實作了。我們不需要寫過多的代碼,隻需要注冊這個監聽,就能很好的監聽屬性的值發生變化。Kvo是Cocoa的一個重要機制,他提供了觀察某一屬性變化的方法,極大的簡化了代碼。這種觀察-被觀察模型适用于這樣的情況,比方說根據A(資料類)的某個屬性值變化,B(view類)中的某個屬性做出相應變化。對于推崇MVC的cocoa而言,kvo應用的地方非常廣泛。(這樣的機制聽起來類似Notification,但是notification是需要一個發送notification的對象,一般是notificationCenter,來通知觀察者。而kvo是直接通知到觀察對象。)
适用kvo時,通常遵循如下流程:
1 注冊:
- ( void )addObserver:(NSObject * )anObserverforKeyPath:(NSString * )keyPathoptions:(NSKeyValueObservingOptions)optionscontext:( void * )context
keyPath就是要觀察的屬性值,options給你觀察鍵值變化的選擇,而context友善傳輸你需要的資料(注意這是一個void型)。
options是監聽的選項,也就是說明監聽泛黃的額字典包含什麼值。有兩個常用的選項:
NSKeyValueObservingOptionsNew指傳回的字典包含新值。
NSKeyValueObservingOptionsOld值傳回的字典包含舊值。
2 實作變化方法:
- ( void )observeValueForKeyPath:(NSString * )keyPathofObject:(id) object
change:(NSDictionary * )changecontext:( void * )context change裡存儲了一些變化的資料,比如變化前的資料,變化後的資料;如果注冊時context不為空,這裡context就能接收到。
其實,知道了kvo的邏輯隻是幫助你了解而已,要真正掌握的,不在于kvo的實作步驟是什麼,而在于KVC,因為隻有符合KVC标準的對象才能使用kvo(強烈推薦要使用kvo的人先了解KVC)。
給下面一個例子:
- 1 .person類
- @implementation Person
- @synthesize name,age;//屬性name 将被監視
- -(void) changeName
- {
- [email protected]"changeName directly";
- }
- @end
- 2.PersonMonitor類 監視了name屬性
- @implementation PersonMonitor
- - (void)observeValueForKeyPath:(NSString *)keyPath
- ofObject:(id)object
- change:(NSDictionary *)change
- context:(void *)context
- {
- if ([keyPath isEqual:@"name"])
- {
- NSLog(@"change happen, old:%@ new:%@",[change objectForKey:NSKeyValueChangeOldKey],[change objectForKey:NSKeyValueChangeNewKey]);
- }
- }
- @end
- 3測試代碼
- //初始化被監視對象
- Person *p =[[Person alloc] init];
- //監視對象
- PersonMonitor *pm= [[PersonMonitor alloc]init];
- [p addObserver:pm forKeyPath:@"name" options:(NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld) context:nil];
- //測試前的資料
- NSLog(@"p.name is %@",p.name);
- //通過setvalue 的方法,PersonMonitor的監視将被調用
- [p setValue:@"name kvc" forKey:@"name"];
- //檢視設定後的值
- NSLog(@"p name get by kvc is %@",[p valueForKey:@"name"]);
- //效果和通過setValue 是一緻的
- [email protected]"name change by .name=";
- //通過person自己的函數來更改name
- [p changeName];
- 結果是
- 輸出
- 2011-07-03 16:35:57.406 Cocoa[13970:903] p.name is name
- 2011-07-03 16:35:57.418 Cocoa[13970:903] change happen, old:name new:name kvc
- 2011-07-03 16:35:57.420 Cocoa[13970:903] p name get by kvc is name kvc
- 2011-07-03 16:35:57.421 Cocoa[13970:903] change happen, old:name kvc new:name change by .name=
- 最後一次修改是直接修改 是以沒法産生通知
3、通知(Notification)
通知是一種發送給一個或者多個觀察者,用來通知其在程式中發生了某個事件的消息。Cocoa中的通知機制遵循的是一種廣播的模式。它是一種程式中事件的發起者或者是處理者和其他想要知道該事件的對象溝通的一種方式。消息的接收者,也就是觀察者響應該事件來變換自己的UI,行為或者是狀态。發送通知的對象沒有必要知道這些觀察者都是誰。是以,通知時一種在程式中可以獲得高效協作同時保持較高内聚性的機制。他減少了程式中對象互相之間的強依耐性(這種依耐性會大大降低程式中代碼的可複用性)。基礎庫,AppKit以及其他的一些Objective-C架構中的很多類都定義了通知以便我們可以注冊成為通知的觀察者。
通知機制的核心就是一個程序中單一執行個體的對象,被叫做通知中心(NSNotificationCenter)。當一個對象釋出一個通知時,通知會先被釋出到通知中心。通知中心的作用相當于是交流所,作為通知的廣播中心。程式中其他需要感覺該事件的對象通過向通知中心注冊就可以達到在事件發生時被通知中心及時通知到得目的。通知中心是可以以同步的方式向其觀察者發送通知,也是可以通過使用通知隊列(NSNotificationQueue)來異步地發送通知。
用于表示通知的對象
一個通知是用NSNotification類的一個對象來表示的。表示通知的對象中含有用于表示該通知的資訊的字段:通知的名稱,釋出通知的對象以及一個用于表示其他補充資訊的字典。這個字典被稱為是userInfo字典。當通知被發送給對其感興趣的觀察者時,該表示通知的對象會被作為參數傳入到處理該通知的方法中。
觀察指定的通知
實作觀察某個通知的時候,我們先要擷取NSNotificationCenter的單執行個體對象并向其發送addObserver:selector:name:object:消息。通常情況下,這種注冊的行為在應用程式啟動後就要進行。addObserver:selector:name:object:方法的第二個參數是一個選擇器,該選擇器指定的是處理指定通知的具體方法。方法的原型必需如下:
-(void)myNotificationHandler:(NSNotification*)notif;
在該方法中,我們可以提取通知中的相關資訊來幫助我們處理資料,特别是userInfo中的資料(如果userInfo存在的話)。
釋出通知
通常在釋出通知之前,我們都需要定義一個全局的字元串常量來作為通知的名稱。傳統的做法是采用和應用程式相關的兩個或者三個字母作為通知名稱的字首,例如:
NSString & AMMyNotification = @”AMMyNotification”;
通過向NSNotificationCenter的單執行個體對象發送postNotificationName:object:userInfo:(或者類似的)消息來完成通知的釋出。在向通知中心發送通知之前,該方法會先建立一個表示該通知的對象。
總結如下:
1、獲得通知中心對象
NSNotificationCenter *center=[NSNotificationCenter defaultCenter]; //單例實際是獲得通知中心的位址
2、監聽通知
[center addObserver :監聽者selector:須執行的方法name:所監聽者通知的名稱object:通知發送者];
3、通知中心釋出消息
[centerPostNotificationName:@"國王萬歲"object:某人];
4.移除監聽中心
[center removeObserver:self name:@"國王萬歲"object:某人];
Demo
King *king= [[King alloc]init];
Farmer*farmer = [[Farmer alloc]init];
NSNotificationCenter *center = [NSNotificationCenterdefaultCenter];
[centeraddObserver:farmer selector:@selector(test:) name:@"國王萬歲"object:king];
[centerpostNotificationName:@"國王萬歲"object:king];
[centerremoveObserver:farmer];
[kingrelease];
[farmerrelease];
-(void)test:(NSNotification*)n //方法必須實作在監聽通知的類裡面 做想做的事
{
NSLog(@"name:%@ object:%@userInfo:%@",[n name],[nobject],[n userInfo]);
}