標籤:style blog http color 資料 2014
原文連結:obj-c編程15[Cocoa執行個體02]:KVC和KVO的實際運用
我們在第16和第17篇中分別介紹了obj-c的KVC與KVO特性,當時舉的例子比較fun,太抽象,貌似和實際不沾邊哦。那麼下面我們就用一個實際中的例子來看看KVC與KVO是如何運用的吧。
該例中用到了3種新的控制項類型:NSTableView、NSSlider以及簡單的NSTextField類型。按說不能再在Random類裡添加不沾邊的新增功能了,但是為了簡單,我還是把所有東西都放在Random類裡嘍。程式運行時介面如下:
大家可以看到左上方的文本域控制項用來顯示當前音量,因為它和Random類裡的str_volume(或者是str_vol_way2以及str_vol_way3)屬性做了綁定,所以它會即時更新音量變化的數值;而文本域控制項下方的刻度條控制項可以讓使用者更改音量大小(從0 到 100),它和Random類的屬性volume綁定起來,所以改變刻度就會帶來volume屬性值的變化,相當於對volume屬性做寫者操作哦。有童鞋可能不明白文本域是如何隨著刻度條值的變化而變化的,畢竟他們綁定的不是一個屬性啊!
其實我在這裡用了KVO的概念,不過這裡我用自己監視的是自己的屬性啊:在Random的init方法中設定被觀察者為self,而觀察者也是self,觀察的KeyPath為@"volume",於是乎若刻度條將volume改變(因為它和volume綁定,所以它刻度的改變會導致volume的改變,上面已經說了,會調用volume的寫者方法。),則self,也就是Random的對象自己會收到通知。不過這裡要注意,不能夠直接在通知回調方法中直接寫:str_volume = [新值] ,因為你這樣沒有調用str_volume的寫者方法,你是直接改執行個體變數本身了,這樣文本域不會有變化的:因為文本域控制項也對str_volume做了KVO,而且該KVO只能監視到str_volume被寫者方法改變的情況,你直接改它執行個體變數,自然沒有反應嘍。要解決這個問題,可以有3種方法:
1 將str_volume定義在interface,並聲明屬性,然後在implementation裡做@synthesize str_volume,這樣我們用[self setStr_volume或self.str_volume的方法(執行個體變數str_volume的寫者方法)修改,自然會通知外部監控器。
2 手動發送通知,告知屬性被改了,這也是str_vol_way2的方法;
3 用屬性的KVC方式修改其值,這也是str_vol_way3的方法啊(注意:str_vol_way3沒有外部介面哦!)。
然後在人語發聲之前用speech的setVolume方法應用當前的音量大小就可以啦,不過注意該方法setVolume的參數是浮點數,取值範圍為0.0 - 1.0,所以我在代碼中要除以100啊!
再看視窗右上方的列表視圖控制項,也有幾個地方要注意:
1 它是由幾個控制群組成的,滑鼠要多選幾次才能選中裡面的控制項,如果控制項選的不對,可能就找不到要設定屬性嘍:
2 其Connections Inspector中要串連2個地方:dataSource和delegate。前者用來做資料來源的代理,後者做其本身動作的代理哦。如果不設定前者則沒有資料來源,就沒東西顯示啊;同理,若不設定後者就無法響應使用者的動作哦。切記切記。這裡再說說代理,代理就是你調用別的類,可是有些事還是你自己最清楚,所以別的類的有些操作還是得返回來問你自己啊。比如列表視圖控制項對於[col,row]位置顯示的內容是不知道的,所以你必須以回調方法的方式告訴它;再者,如果它的當前選中行發生變化了,他也不知道如何處理,所以也要問你,等於是一個當前行改變的事件發生了,Random類必須提供事件處理函數哦。
最後,NSTableView控制項還是要按老規矩和Random類串連起來啊,就在其Referencing Outlets裡哦;也就是說它和Random一共發生了3種顯式關係(想歪的自覺面壁去)如所示:
好了,上面把主要的問題都大致說過了,啥也不說鳥,下面的都在代碼裡嘍(在Cocoa執行個體02的代碼基礎上修改而來):
1 // 2 // Random.h 3 // mac_test 4 // 5 // Created by kinds on 14-7-4. 6 // Copyright (c) 2014年 kinds. All rights reserved. 7 // 8 9 #import "comm.h"10 #import <Cocoa/Cocoa.h>11 12 @interface Random : NSObject {13 IBOutlet NSTextField *text_field;14 IBOutlet NSTableView *tab_view;15 NSString *str_volume;16 }17 18 @property NSString *str_volume;19 20 -(IBAction)seed:(id)sender;21 -(IBAction)generate:(id)sender;22 23 @end
1 // 2 // Random.m 3 // mac_test 4 // 5 // Created by kinds on 14-7-4. 6 // Copyright (c) 2014年 kinds. All rights reserved. 7 // 8 9 #import "Random.h" 10 11 @implementation Random{ 12 NSSpeechSynthesizer *speech; 13 NSArray *voices; 14 NSNumber *volume; 15 NSString *str_vol_way2; 16 NSString *str_vol_way3; 17 } 18 19 @synthesize str_volume; 20 21 -(id)init{ 22 self = [super init]; 23 if(self){ 24 speech = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; 25 voices = [NSSpeechSynthesizer availableVoices]; 26 msg(@"%@",voices); 27 28 volume = [NSNumber numberWithInt:0]; 29 str_volume = [NSString stringWithFormat:@"音量:%@",volume]; 30 str_vol_way2 = str_vol_way3 = str_volume; 31 [self addObserver:self forKeyPath:@"volume" 32 options:NSKeyValueObservingOptionNew context:nil]; 33 } 34 return self; 35 } 36 37 -(void)observeValueForKeyPath:(NSString *)key_path ofObject:(id)obj 38 change:(NSDictionary *)change context:(void *)context{ 39 NSNumber *new_val = [change objectForKey:NSKeyValueChangeNewKey]; 40 msg(@"volume is change to %@",new_val); 41 //str_volume = @"A"; 42 self.str_volume = [NSString stringWithFormat:@"音量:%i",[new_val intValue]]; 43 44 //syn way 2 45 [self willChangeValueForKey:@"str_vol_way2"]; 46 str_vol_way2 = str_volume; 47 [self didChangeValueForKey:@"str_vol_way2"]; 48 //syn way 3 49 [self setValue:str_volume forKey:@"str_vol_way3"]; 50 } 51 52 -(void)set_voice{ 53 int idx = (int)(random() % [voices count]); 54 [speech setVoice:[voices objectAtIndex:(NSUInteger)idx]]; 55 } 56 57 -(IBAction)generate:(id)sender{ 58 int i = (int)(random() % 100000000000) + 1; 59 msg(@"i = %d",i); 60 [text_field setIntValue:i]; 61 [self set_voice]; 62 //[speech setVolume:[volume floatValue]]; 63 [speech startSpeakingString:[NSString stringWithFormat:@"%i",i]]; 64 } 65 66 -(IBAction)seed:(id)sender{ 67 srandom((unsigned)time(NULL)); 68 NSString *str = @"the seed is reseted!"; 69 [text_field setStringValue:str]; 70 [speech startSpeakingString:str]; 71 } 72 73 -(void)awakeFromNib{ 74 NSDate *now = [NSDate date]; 75 [text_field setObjectValue:now]; 76 } 77 78 -(NSInteger)numberOfRowsInTableView:(NSTableView *)tv{ 79 return (NSInteger)[voices count]; 80 } 81 82 -(id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)col 83 row:(NSInteger)row{ 84 NSString *v = [voices objectAtIndex:row]; 85 return v; 86 } 87 88 -(void)tableViewSelectionDidChange:(NSNotification *)notification{ 89 NSInteger row = [tab_view selectedRow]; 90 if(row == -1) return; 91 92 NSString *str_voice = [voices objectAtIndex:row]; 93 [speech setVoice:str_voice]; 94 [speech setVolume:[volume floatValue]/100]; 95 [speech startSpeakingString:@"test one time!!!測試一下哦!!!"]; 96 msg(@"new voice = %@",str_voice); 97 } 98 99 -(void)dealloc{100 [self removeObserver:self forKeyPath:@"volume"];101 }102 103 104 @end