obj-c編程17:索引值觀察(KVO)

來源:互聯網
上載者:User

標籤:objecitve-c   索引值觀察   kvo   

    說完了前面一篇KVC,不能不說說它的應用KVO(Key-Value Observing)嘍。KVO類似於ruby裡的hook功能,就是當一個對象屬性發生變化時,觀察者可以跟蹤變化,進而觀察或是修正這個變化,這是通過回調觀察者註冊的回呼函數來完成的。要使用鍵值觀察,必須滿足3個條件:

1 被觀察對象必須對所觀察屬性使用符合KVC標準的存取器方法;

2 觀察者必須實現接受通知的方法(回調方法):-observeValue:forKeyPath:ofObject:change:context:,該方法在值發生變化時被調用,並且可以定製行為:比如同時接收新值和原值以及其他自訂資訊;

3 觀察者通過-addObserver:forKeyPath:options:context:方法對特定對象的特定路徑進行註冊.

還要注意的是,在觀察者完成對被觀察者的觀察後,必須將自己移除,否則當觀察者釋放後,來自被觀察者的變化通知訊息將無處可發,可能會導致引用崩潰。另外可以使用多個觀察者對同一個對象的同一個路徑進行觀察啊,類似於形成一個觀察鏈。

    下面我將會寫一個簡單的例子,用代碼說明上述內容:

#import <Foundation/Foundation.h>#define msg(...) NSLog(__VA_ARGS__)#define mki(x) [NSNumber numberWithInt:x]@interface Son:NSObject{NSString *girl_friend_name;}@property NSString *girl_friend_name;-(void)think;-(void)fall_in_love_with:(NSString *)girl_name;@end@implementation Son@synthesize girl_friend_name;-(void)fall_in_love_with:(NSString *)girl_name{self.girl_friend_name = girl_name;}-(void)think{msg(@"My love girl is %@ ... Does baba know?",girl_friend_name);}@end@interface Baba:NSObject{Son *son;}@property Son *son;@end@implementation Baba@synthesize son;-(id)initWithSon:(Son *)son_v{self = [super init];if(self){son = son_v;[son addObserver:self forKeyPath:@"girl_friend_name" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionInitial) context:nil];}return self;}-(void)observeValueForKeyPath:(NSString *)key_path ofObject:(id)obj change:(NSDictionary *)change context:(void *)context{NSString *old_name = [change objectForKey:NSKeyValueChangeOldKey];NSString *new_name = [change objectForKey:NSKeyValueChangeNewKey];if(!old_name){//NSKeyValueObservingOptionInitial選項的作用,第一次hook會發一次訊息//以後每次更改會發一次訊息。msg(@"Yes , It's first time to induction son's girl_name!!!");}else{if([new_name isEqualToString:@"libinbin"]){[obj fall_in_love_with:@"fanbinbin"];}msg(@"My son's %@ change from %@ to %@ ... That's OK? :(",key_path,old_name,new_name);}}-(void)dealloc{[son removeObserver:self forKeyPath:@"girl_friend_name"];son = nil;//[super dealloc];}@endint main(int argc,char *argv[]){@autoreleasepool{Son *son = [[Son alloc] init];[son fall_in_love_with:@"lili"];[son think];Baba *baba = [[Baba alloc] initWithSon:son];[son fall_in_love_with:@"mimi"];[son think];//兒子本來喜歡的是libinbin,可是...[son fall_in_love_with:@"libinbin"];//被他老爸用特異功能改成fanbinbin啦![son think];}return 0;}

簡單說明下:老爸自然是要隨時監控兒子的女朋友啊,可是這個老爸監視也就算了,還強行改變兒子的女朋友,這個就...隨便想的一個例子,就當fun吧。說明下主要方法:

-addObserver註冊對象的觀察者,其中的options的其他可選值如下,可以多選:


其中的context參數意義不明哦,如果哪位知道的,別忘了告訴老貓我一下啊。NSKeyValueObservingOptionInitial標誌的含義參見代碼注釋吧。

-observeValueForKeyPath回調方法中的change是一個字典參數,其中包括:


其中NSKeyValueChangeKindKey指定了接收到得變化類型,可能有以下幾種值:


書上的代碼在該方法定義的末尾部分增加了其對父類中同名方法的回溯調用:

[super observeValueForkeyPath:key_path ofObject:obj change:change context:ctx];

不過我在代碼中沒有寫這個。書上在觀察者的dealloc方法最後是有一句[super dealloc],但是這招在ARC下編譯時間會出錯嘍,暫時去掉吧。該段代碼運行結果如下:

[email protected]: objc_src$./k

2014-07-06 20:11:26.757 k[2109:507] My love girl is lili ... Does baba know?

2014-07-06 20:11:26.759 k[2109:507] Yes , It‘s first time to induction son‘s girl_name!!!

2014-07-06 20:11:26.760 k[2109:507] My son‘s girl_friend_name change from lili to mimi ... That‘s OK? :(

2014-07-06 20:11:26.760 k[2109:507] My love girl is mimi ... Does baba know?

2014-07-06 20:11:26.761 k[2109:507] My son‘s girl_friend_name change from libinbin to fanbinbin ... That‘s OK? :(

2014-07-06 20:11:26.761 k[2109:507] My son‘s girl_friend_name change from mimi to libinbin ... That‘s OK? :(

2014-07-06 20:11:26.761 k[2109:507] My love girl is fanbinbin ... Does baba know?

    爸爸監管的不錯,可是兒子不高興嘍。hack兒子如何能讓老爸如此侵犯隱私啊?咋辦呢?不自動傳遞變更通知不就結了!為了實現手動通知,需要重寫Son的類方法+automaticallyNotifiesObserversForKey,來告知obj-c你不想自動通知觀察者自己的變化,注意是類方法哦!可以通過對特定的鍵名返回NO來完成:

+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{if([key isEqualToString:@"girl_friend_name"])return NO;return YES;}

在Son類的實現中加入以上類方法,重新編譯運行:

[email protected]: objc_src$./k

2014-07-06 20:37:36.726 k[2180:507] My love girl is lili ... Does baba know?

2014-07-06 20:37:36.729 k[2180:507] Yes , It‘s first time to induction son‘s girl_name!!!

2014-07-06 20:37:36.729 k[2180:507] My love girl is mimi ... Does baba know?

2014-07-06 20:37:36.730 k[2180:507] My love girl is libinbin ... Does baba know?

老爸”感應“不到嘍!但是這樣長了老爸會懷疑的啦,腫麼寶貝兒子從不談戀愛呢?這個不行哦!於是乎聰明的hack兒子可以再手動的通知老爸篡改後的內容啊!具體為:在Son屬性寫者方法的變化之前調用-willChangeValueForKey,然後在變化之後調用-didChangeValueForKey,重新改寫寫者方法吧,修改後完整代碼如下:

#import <Foundation/Foundation.h>#define msg(...) NSLog(__VA_ARGS__)#define mki(x) [NSNumber numberWithInt:x]@interface Son:NSObject{NSString *girl_friend_name;}//在屬性預設為atomic的情況下,如果自訂寫者方法必須同時自訂讀者方法,//或者將屬性改為nonatomic模式。@property(nonatomic) NSString *girl_friend_name;-(void)think;-(void)fall_in_love_with:(NSString *)girl_name;@end@implementation Son@synthesize girl_friend_name;-(void)fall_in_love_with:(NSString *)girl_name{self.girl_friend_name = girl_name;}-(void)think{msg(@"My love girl is %@ ... Does baba know?",girl_friend_name);}+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{if([key isEqualToString:@"girl_friend_name"])return NO;return YES;}-(void)setGirl_friend_name:(NSString *)name{[self willChangeValueForKey:@"girl_friend_name"];//讓老爸以為自己和女學霸談戀愛哦girl_friend_name = @"女學霸";[self didChangeValueForKey:@"girl_friend_name"];//恢複本色吧,BAD BOY... :)girl_friend_name = name;}@end@interface Baba:NSObject{Son *son;}@property Son *son;@end@implementation Baba@synthesize son;-(id)initWithSon:(Son *)son_v{self = [super init];if(self){son = son_v;[son addObserver:self forKeyPath:@"girl_friend_name" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionInitial) context:nil];}return self;}-(void)observeValueForKeyPath:(NSString *)key_path ofObject:(id)obj change:(NSDictionary *)change context:(void *)context{NSString *old_name = [change objectForKey:NSKeyValueChangeOldKey];NSString *new_name = [change objectForKey:NSKeyValueChangeNewKey];if(!old_name){//NSKeyValueObservingOptionInitial選項的作用,第一次hook會發一次訊息//以後每次更改會發一次訊息。msg(@"Yes , It's first time to induction son's girl_name!!!");}else{if([new_name isEqualToString:@"libinbin"]){[obj fall_in_love_with:@"fanbinbin"];}msg(@"My son's %@ change from %@ to %@ ... That's OK? :(",key_path,old_name,new_name);}}-(void)dealloc{[son removeObserver:self forKeyPath:@"girl_friend_name"];son = nil;//[super dealloc];}@endint main(int argc,char *argv[]){@autoreleasepool{Son *son = [[Son alloc] init];[son fall_in_love_with:@"lili"];[son think];Baba *baba = [[Baba alloc] initWithSon:son];[son fall_in_love_with:@"mimi"];[son think];//兒子本來喜歡的是libinbin,可是...[son fall_in_love_with:@"libinbin"];//被他老爸用特異功能改成fanbinbin啦![son think];}return 0;}

編譯運行結果如下:

[email protected]: objc_src$./k

2014-07-06 20:58:59.284 k[2270:507] My love girl is lili ... Does baba know?

2014-07-06 20:58:59.286 k[2270:507] Yes , It‘s first time to induction son‘s girl_name!!!

2014-07-06 20:58:59.287 k[2270:507] My son‘s girl_friend_name change from lili to 女學霸 ... That‘s OK? :(

2014-07-06 20:58:59.287 k[2270:507] My love girl is mimi ... Does baba know?

2014-07-06 20:58:59.288 k[2270:507] My son‘s girl_friend_name change from mimi to 女學霸 ... That‘s OK? :(

2014-07-06 20:58:59.288 k[2270:507] My love girl is libinbin ... Does baba know?

這回該老爸傻傻的以為兒子一直在和女學霸貪戀嘍,各位童鞋當年也是這樣糊弄老爸老媽的吧,嘿嘿。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.