12

誰かが私が次の方法が何をしているのか理解するのを手伝ってもらえますか?

+ (Game *) shared
{
    static Game *sharedSingleton;

    @synchronized(self)
    {
        if (!sharedSingleton)
        {
            sharedSingleton = [[Game alloc] init];
        }
    }

    return sharedSingleton;
}
4

4 に答える 4

46

明らかに、シングルトンの背後にある考え方は、単一のインスタンスのみを作成することです。これを実現するための最初のステップは、行を介してクラスの静的インスタンスを宣言することstatic Game *sharedSingleton;です。

2番目のステップは、単一インスタンスがすでに作成されているかどうかを確認し、作成されていない場合は作成するか、作成されている場合は既存の単一インスタンスを返すことです。+sharedただし、この2番目のステップでは、2つの別々のスレッドが同じ瞬間にメソッドを呼び出そうとした場合に、問題が発生する可能性があります。sharedSingleton予期しない結果が生じる可能性があるため、別のスレッドが単一の変数を調べようとしているときに、あるスレッドが単一の変数を変更することは望ましくありません。

この問題の解決策は、@synchronized()コンパイラ指令を使用して、括弧で囲まれたオブジェクトへのアクセスを同期することです。たとえば、クラスのこの単一の共有インスタンスに、Gameクラスのインスタンスのであるという名前のインスタンス変数がplayersあるNSMutableArrayとしますPlayerGameクラスに、指定されたプレーヤーを追加することでインスタンス変数-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

@implementationstatic

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、およびがsharedInstance1回だけ作成されることを保証します。(ただし、誰かがのようなものを使用して非シングルトンインスタンスを作成できないようにするには、さらに予防策が必要になることに注意してくださいGame *game = [[Game alloc] init];)。

于 2011-06-04T04:56:42.090 に答える
9

行ごとの説明..。

// A static variable guarantees there's only 1 instance of it ever, 
// even accross multiple instances of the same class, this particular
// variable will store the class instance, so it can be returned whenever
// a client-class requests an instance of this class.
static Game *sharedSingleton;

// create a method that can always be called, even if there's no instance yet
// this method should create a new instance if there isn't one yet, otherwise
// return the existing instance
+ (Game *) shared  
{
    // synchronized makes sure only 1 client class can enter this method at any time, 
    // e.g. to prevent creating 2 instances whenever 2 client-classes try to 
    // access the following code from different threads.
    @synchronized(self)
    {
        // if the static variable is called for the first time, 
        // create an instance and return it, otherwise return the existing instance ...
        if (!sharedSingleton)
        {
            sharedSingleton = [[Game alloc] init];
        }
    }

    return sharedSingleton;
}
于 2011-06-04T07:50:40.270 に答える
3

Objective-Cについてはよくわかりませんが、Game *タイプのシングルトンコピーを取得するための方法のようであり、スレッドセーフであることが意図されています。

基本的に、そのメソッドを呼び出すと、(どのスレッドに関係なく)毎回Game *の同じコピーが返され、最初に呼び出されるまでGameの新しいインスタンスが割り当てられません(遅延読み込みと呼ばれます) 。

于 2011-06-04T02:40:40.610 に答える
3

シングルトンがコピーされるのを避けるために、「mutableCopyWithZone:」メソッドと「copyWithZone:」メソッドもオーバーライドします。

// copy cannot be done
- (Game *)copyWithZone:(NSZone *)zone {
    return self;
}
// mutablecopy cannot be done
- (Game *)mutableCopyWithZone:(NSZone *)zone {
    return [self copyWithZone:zone];
}

'copy'と'mutableCopy'(シングルトンクラスがデフォルトのNSObject実装をオーバーライドした別のクラスから継承しないと仮定)なので、'copyWithZone:'と'mutableCopyWithZone:'を呼び出すだけです。これらもオーバーライドする必要はありません。

また、他の開発者が「init」または「new」を使用してシングルトンパターンを壊さないようにするために、2つのメソッドを使用不可と宣言できます。

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

このように、コンパイラは2つのメソッドの使用を許可せず、他の開発者は共有インスタンスを取得するために文書化されたメソッドを使用することを余儀なくされます。もう1つの(より厳密な)手法は、「init」と「new」をオーバーライドして例外を発生させることです。

于 2017-02-27T00:44:27.363 に答える