編集
これを上に含めます。下に、私の歴史的な元の質問と実装を示します。ただし、ロックのオーバーヘッドなしで sharedInstance メソッドを提供する最適な方法を見つけたと思います。これに関する潜在的な懸念をぜひお聞かせください。
// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;
// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
return (MySingleton *)sharedInstance;
}
+ (MySingleton*)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [[MySingleton alloc] init];
// Replace expensive thread-safe method
// with the simpler one that just returns the allocated instance.
SEL orig = @selector(sharedInstance);
SEL new = @selector(simpleSharedInstance);
Method origMethod = class_getClassMethod(self, orig);
Method newMethod = class_getClassMethod(self, new);
method_exchangeImplementations(origMethod, newMethod);
}
}
return (MySingleton *)sharedInstance;
}
そして、初期化に関する歴史的な議論:
ロックの外側のインスタンスをチェックすることを除いて、元のコードは実際には私のコード (以下) にかなり似ていました。
新しい + (void) 初期化メソッドは興味深いものですが、これの方が好きかどうかはわかりません。シングルトンインスタンスを取得するには、常に呼び出す必要があるようです。
MySingleton instance = [[MySingleton alloc] init];
それは正しくありませんか?それは奇妙に感じます。initialize の呼び出しが既にロックされている場合は、より効率的ですか? 二重ロック方法は、このユースケースでは問題なく機能するようですが、ロックも回避します (複数のスレッドが if を通過する可能性があるため、二重割り当ての潜在的なコストがかかると思います)。
奇妙に思えるもう 1 つの点は、initialize メソッドが本当に好まれるのであれば、なぜ他の場所で見られないのでしょうか? Objective-C は長い間存在しており、公開されているほぼすべての例とは異なる基本的なメカニズムに警戒しています。
現在使用している私のコード(この回答を含め、他の場所で見たものを反映しています):
+ (MySingleton *)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [super allocWithZone:zone];
return sharedInstance; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}