シングルトンにする
これまでのやり方
これまでRandomizerクラスのオブジェクトはMainMenu.nib内でインスタンス化していました。アプリケーション内に一つ存在すれば十分なのでそのようにしていたのですが、こうするとRMGDocument.nib内のオブジェクトからRandomizerクラスのオブジェクトにメッセージを送るのが少し面倒でした。
アウトレットを接続できれば簡単なのですが、RMGDocument.nib内のオブジェクトとMainMenu.nib内のオブジェクトをアウトレットで接続する事は無理です。しかたがないので通知を使っていましたが、Randomizerクラスのオブジェクトに送るメッセージの種類が増えてくると、それに応じて通知の種類も増やさなくてはならないので、これから大変になりそうです。
一つ存在すれば十分ならシングルトンにする
Cocoaが提供するクラスの中にはシングルトンデザインパターンを使っているものがあります。例えばドキュメントコントローラやファイルマネージャがそうです。したがって使い方はわかっている訳ですが、Randomizerをシングルトンにすればクラスメソッドでインスタンスオブジェクトにアクセスできるので、アウトレットや通知に頼らずにメッセージを送る事ができ、上記の問題を解決できそうです。
シングルトンを実現する方法
ではどうやってシングルトンデザインパターンを実現するのでしょうか?この情報はアップルの「シングルトンインスタンスの作成」に載っているので、これを参考にしました。また「ダイナミックObjective-C」の第50〜52回も参考になると思います。
やっている事を箇条書きにすると、以下の様になります。
- シングルトンオブジェクトを入れるstatic変数を宣言し、nilに初期化する
- シングルトンオブジェクトを返すクラスメソッドsharedRandomizerで、static変数がnilの場合のみ、クラスのインスタンスオブジェクトを作成する
- allocWithZone:メソッドをオーバーライドし、sharedRandomizerを使わずにRandomizerクラスのインスタンスオブジェクトを直接作ろうとした時でもシングルトンオブジェクトを返す様にする
- copyWithZone:、retain、autoreleaseはシングルトンの状態を保つため、何もせずにselfを返す
- releaseはシングルトンの状態を保つため、何もしない
- retainCountは解放できないオブジェクトである事を示すため、unsigned intの最大値UINT_MAXを返す
#pragma mark ----- singleton design pattern -----
static id rmgRandomizer = nil;
+ (id)sharedRandomizer
{
@synchronized(self)
{
if(!rmgRandomizer)
[[self alloc] init];
}
return rmgRandomizer;
}
+ (id)allocWithZone:(NSZone*)zone
{
@synchronized(self)
{
if(!rmgRandomizer)
{
rmgRandomizer = [super allocWithZone:zone];
return rmgRandomizer;
}
}
return nil;
}
- (id)copyWithZone:(NSZone*)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX;
}
- (void)release
{
}
- (id)autorelease
{
return self;
}
ちなみに@synchronized()ディレクティブは、同時に複数のスレッドによって実行されないようにコードの一部を保護するために使うものだそうです。
@synchronized()ブロックの最後の文を通過するまで他のスレッドはブロックされるので、違うスレッドから同じタイミングでallocWithZone:メソッドが呼ばれた場合でも二つのインスタンスオブジェクトを作ってしまう事はなく、シングルトンの状態が保たれる事が保証されます。
インターフェイスファイルの変更
sharedRandomizerメソッドは外部から呼び出されるので、コンパイル時に警告が出ない様にインターフェイスファイルに記述しておきます。
@class MasterMotif;
@interface Randomizer : NSObject
{
int amplitudeMinX1,amplitudeMinX2,amplitudeMinX3;
int amplitudeMaxX1,amplitudeMaxX2,amplitudeMaxX3;
int amplitudeMinY1,amplitudeMinY2,amplitudeMinY3;
int amplitudeMaxY1,amplitudeMaxY2,amplitudeMaxY3;
int frequencyMinX1,frequencyMinX2,frequencyMinX3;
int frequencyMaxX1,frequencyMaxX2,frequencyMaxX3;
int frequencyMinY1,frequencyMinY2,frequencyMinY3;
int frequencyMaxY1,frequencyMaxY2,frequencyMaxY3;
}
+ (id)sharedRandomizer;
- (void)randomizeMasterMotif:(MasterMotif *)masterMotif
withUndoManager:(NSUndoManager *)undoManager
lockFrequency:(BOOL)isLockFrequency;
@end