標籤:objecitve-c cocoa編程 kvo kvc
我們在第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的代碼基礎上修改而來):
//// Random.h// mac_test//// Created by kinds on 14-7-4.// Copyright (c) 2014年 kinds. All rights reserved.//#import "comm.h"#import <Cocoa/Cocoa.h>@interface Random : NSObject {IBOutlet NSTextField *text_field;IBOutlet NSTableView *tab_view;NSString *str_volume;}@property NSString *str_volume;-(IBAction)seed:(id)sender;-(IBAction)generate:(id)sender;@end
//// Random.m// mac_test//// Created by kinds on 14-7-4.// Copyright (c) 2014年 kinds. All rights reserved.//#import "Random.h"@implementation Random{NSSpeechSynthesizer *speech;NSArray *voices;NSNumber *volume;NSString *str_vol_way2;NSString *str_vol_way3;}@synthesize str_volume;-(id)init{self = [super init];if(self){speech = [[NSSpeechSynthesizer alloc] initWithVoice:nil];voices = [NSSpeechSynthesizer availableVoices];msg(@"%@",voices);volume = [NSNumber numberWithInt:0];str_volume = [NSString stringWithFormat:@"音量:%@",volume];str_vol_way2 = str_vol_way3 = str_volume;[self addObserver:self forKeyPath:@"volume" options:NSKeyValueObservingOptionNew context:nil];}return self;}-(void)observeValueForKeyPath:(NSString *)key_path ofObject:(id)obj change:(NSDictionary *)change context:(void *)context{NSNumber *new_val = [change objectForKey:NSKeyValueChangeNewKey];msg(@"volume is change to %@",new_val);//str_volume = @"A";self.str_volume = [NSString stringWithFormat:@"音量:%i",[new_val intValue]];//syn way 2[self willChangeValueForKey:@"str_vol_way2"];str_vol_way2 = str_volume;[self didChangeValueForKey:@"str_vol_way2"];//syn way 3[self setValue:str_volume forKey:@"str_vol_way3"];}-(void)set_voice{int idx = (int)(random() % [voices count]);[speech setVoice:[voices objectAtIndex:(NSUInteger)idx]];}-(IBAction)generate:(id)sender{int i = (int)(random() % 100000000000) + 1;msg(@"i = %d",i);[text_field setIntValue:i];[self set_voice];//[speech setVolume:[volume floatValue]];[speech startSpeakingString:[NSString stringWithFormat:@"%i",i]];}-(IBAction)seed:(id)sender{srandom((unsigned)time(NULL));NSString *str = @"the seed is reseted!";[text_field setStringValue:str];[speech startSpeakingString:str];}-(void)awakeFromNib{NSDate *now = [NSDate date];[text_field setObjectValue:now];}-(NSInteger)numberOfRowsInTableView:(NSTableView *)tv{return (NSInteger)[voices count];}-(id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)col row:(NSInteger)row{NSString *v = [voices objectAtIndex:row];return v;}-(void)tableViewSelectionDidChange:(NSNotification *)notification{NSInteger row = [tab_view selectedRow];if(row == -1) return;NSString *str_voice = [voices objectAtIndex:row];[speech setVoice:str_voice];[speech setVolume:[volume floatValue]/100];[speech startSpeakingString:@"test one time!!!測試一下哦!!!"];msg(@"new voice = %@",str_voice);}-(void)dealloc{[self removeObserver:self forKeyPath:@"volume"];}@end