もろもろの勉強を兼ねて、一番初めのObjective-Cプログラム (1/3):Cocoaの素、Objective-Cを知ろう(2) - @IT を改変して、KVO で歌詞変更を監視して自動出力する版への書き換えにチャレンジしてみました。
a Singer は a Song を与えられると、自身をオブザーバーとして a Song に登録します。そして、どこかで歌詞が変更されると、その旨の通知を受け、自発的に sing するというカラクリになっている…はずです。^^;
Singer.m
#import <Foundation/Foundation.h> #import <Foundation/NSKeyValueObserving.h> #import <AppKit/AppKit.h> #import <stdio.h> @interface Song : NSObject { NSString *lyrics; } - (NSString *)lyrics; - (void)setLyrics:(NSString *)argLyrics; @end @implementation Song - (NSString *)lyrics { return lyrics; } - (void)setLyrics:(NSString *)argLyrics { lyrics = argLyrics; } @end @interface Singer : NSObject { Song *song; } - (void)setSong:(Song *)argSong; - (void)sing; @end @implementation Singer - (void)setSong:(Song *)argSong { song = argSong; [song addObserver:self forKeyPath:@"lyrics" options:NSKeyValueObservingOptionNew context:NULL]; } - (void)sing { printf("%s\n", [[song lyrics] UTF8String]); } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqual:@"lyrics"]) { [self sing]; } } @end int main(void) { id song; id singer; NSAutoreleasePool *pool; pool = [NSAutoreleasePool new]; song = [[Song alloc] init]; [song setLyrics:@"La La La ..."]; singer = [[Singer alloc] init]; [singer setSong:song]; [singer sing]; [song setLyrics:@"Lu Lu Lu ..."]; [pool release]; return 0; }
$ ls GNUmakefile SingSong.m $ cat GNUmakefile include $(GNUSTEP_MAKEFILES)/common.make TOOL_NAME = SingSong SingSong_OBJC_FILES = SingSong.m include $(GNUSTEP_MAKEFILES)/tool.make $ make Making all for tool SingSong... Compiling file SingSong.m ... Linking tool SingSong ... $ obj/SingSong.exe La La La ... Lu Lu Lu ...
Squeak Smalltalk で似たようなことを書くとすると次のような感じでしょうか。簡単のため、a Song 向けには特にクラスは作らず、a ValueHolder の contents を lyrics として見立て、それを用いています。
Singerクラスの定義
Object subclass: #Singer instanceVariableNames: 'song' Singer >> setSong: newSong song := newSong. song addDependent: self Singer >> sing World findATranscript: nil. Transcript cr; show: song contents Singer >> update: aSymbol aSymbol == #contents ifTrue: [self sing]
実行スクリプト
| singer song | song := ValueHolder new contents: 'La La La ...'. singer := Singer new setSong: song. singer sing. song contents: 'Lu Lu Lu ...'