明らかに、シングルトンの背後にある考え方は、単一のインスタンスのみを作成することです。これを実現するための最初のステップは、行を介してクラスの静的インスタンスを宣言することstatic Game *sharedSingleton;
です。
2番目のステップは、単一インスタンスがすでに作成されているかどうかを確認し、作成されていない場合は作成するか、作成されている場合は既存の単一インスタンスを返すことです。+shared
ただし、この2番目のステップでは、2つの別々のスレッドが同じ瞬間にメソッドを呼び出そうとした場合に、問題が発生する可能性があります。sharedSingleton
予期しない結果が生じる可能性があるため、別のスレッドが単一の変数を調べようとしているときに、あるスレッドが単一の変数を変更することは望ましくありません。
この問題の解決策は、@synchronized()
コンパイラ指令を使用して、括弧で囲まれたオブジェクトへのアクセスを同期することです。たとえば、クラスのこの単一の共有インスタンスに、Game
クラスのインスタンスのであるという名前のインスタンス変数がplayers
あるNSMutableArray
としますPlayer
。Game
クラスに、指定されたプレーヤーを追加することでインスタンス変数-addPlayer:
を変更するメソッドがあるとします。players
そのメソッドが複数のスレッドから呼び出された場合、一度に1つのスレッドのみがplayers
配列を変更できるようにすることが重要です。したがって、そのメソッドの実装は次のようになります。
- (void)addPlayer:(Player *)player {
if (player == nil) return;
@synchronized(players) {
[players addObject:player];
}
}
ディレクティブを使用すると、一度に1つのスレッドのみが変数に@synchronized()
アクセスできるようになります。players
別のスレッドが現在アクセスしているときに1つのスレッドが試行した場合、最初のスレッドは他のスレッドが終了するまで待機する必要があります。
インスタンス変数について話しているときはもっと簡単ですが、クラス自体の単一の作成メソッド内で同じタイプの結果を達成する方法はおそらくあまり明確ではありません。次のコードself
の@synchronized(self)
行のは、基本的にGame
クラス自体と同じです。クラスで同期することにより、回線が1回だけ呼び出されるGame
ことが保証されます。sharedSingleton = [[Game alloc] init];
+ (Game *) shared
{
static Game *sharedSingleton;
@synchronized(self) // assures only one thread can call [Game shared] at a time
{
if (!sharedSingleton)
{
sharedSingleton = [[Game alloc] init];
}
}
return sharedSingleton;
}
[編集]:更新。しばらく前の私のテストに基づくと(そして私は今それを再テストしたばかりです)、以下はすべて同等であるように見えます:
外@implementation
:
Game *sharedInstance;
@implementation Game
+ (Game *)sharedGame {
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[[self class] alloc] init];
}
}
return sharedInstance;
}
@end
外@implementation
、static
:
static Game *sharedInstance;
@implementation Game
+ (Game *)sharedGame {
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[[self class] alloc] init];
}
}
return sharedInstance;
}
@end
内部@implementation
:
@implementation Game
static Game *sharedInstance;
+ (Game *)sharedGame {
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[[self class] alloc] init];
}
}
return sharedInstance;
}
@end
内部+sharedGame
:
@implementation Game
+ (Game *)sharedGame {
static Game *sharedInstance;
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[[self class] alloc] init];
}
}
return sharedInstance;
}
@end
唯一の違いは、最初のバリエーションでは、static
キーワードsharedInstance
がないと、の[ファイルの統計]の下に表示されないことgdb
です。そして明らかに、最後のバリエーションでは、メソッドsharedInstance
の外部には表示されません。+sharedGame
しかし、実際には、それらはすべて、電話をかけたとき[Game sharedInstance]
に戻ってくることsharedInstance
、およびがsharedInstance
1回だけ作成されることを保証します。(ただし、誰かがのようなものを使用して非シングルトンインスタンスを作成できないようにするには、さらに予防策が必要になることに注意してくださいGame *game = [[Game alloc] init];
)。