What should my Objective-C singleton look like?は、シングルトンのための良い議論ですが、あなたの質問に答えるために:あなたはnil
で同期することはできませんので、あなたはクラスオブジェクトで同期をとる、ノー
を動作しないでしょう。どうして?
@synchronizedには、同期するために割り当てられた定数オブジェクトが必要です。それを基準点と考えてください。 _instanceを使用していますが、これは最初はnil(not good)になります。
@synchronized(自己)今
限りの欠陥、あなたはクラス自体で一定のオブジェクトに対して同期たら(:Objective-Cのの素敵な部分は、クラスはあなたが行うことができますので、それ自体がオブジェクトであるということです自己を使用すると)、欠陥のないシングルトンが得られますが、シングルトンにアクセスするたびに同期オーバーヘッドのコストがかかるため、パフォーマンスに大きな影響があります。
これを緩和する簡単なメカニズムは、作成が一度だけ必要であることを識別し、その後すべての呼び出しが同じ参照を返すことです。これはプロセスの生涯にわたって変化しません。
static MySingleton *_instance = nil;
+ (MySingleton *) instance
{
if (!_instance)
{
@synchronized (self)
{
if (!_instance)
{
_instance = [[MySingleton alloc] init];
}
}
}
return _instance;
}
は今、あなたはシングルトンあなたのための二重NULLチェックの実装を持っている:シングルトンを作成した後、我々はそのパフォーマンスヒットを避けるために、nilのチェックであなたの@synchronizationブロックをラップすることができ、これを識別
。ちょっと待って!考慮すべきことがさらにあります!何?何が残ることができますか?このサンプルコードは何もありませんが、シングルトンを作成する際に実行する作業がある場合は、いくつかの考慮事項を加える必要があります。
2番目のnilチェックを見てみましょう追加作業が必要な共通シングルトンシナリオ用の追加コード。
if (!_instance)
{
_instance = [[MySingleton alloc] init];
[_instance performAdditionalPrepWork];
}
このすべてがとてもよく似合う、しかし_INSTANCEリファレンスへのalloc-initedクラスの割り当てと、我々は準備作業を行う地点間の競合状態が存在します。 2番目のスレッドが_instanceが存在することを確認し、準備作業が未定義の(そして潜在的にクラッシュする)結果の競合条件の作成を完了する前に使用することは可能です。
私たちは何をする必要がありますか?さて、シングルトンを_instance参照に割り当てる前に完全に準備しておく必要があります。
if (!_instance)
{
MySingleton* tmp = [[MySingleton alloc] init];
[tmp performAdditionalPrepWork];
_instance = tmp;
}
簡単ですね。私たちはオブジェクトを完全に準備した後まで、シングルトンの_instance参照への割り当てを遅らせることで問題を解決しました。完璧な世界では、そうですが、完璧な世界では生きていないし、完璧なコード...コンパイラを使って外部の力を説明する必要があります。
コンパイラは非常に高度であり、見た目の冗長性を排除することによってコードの効率を向上させるために、冗長性は、_instance参照を直接使用することによって一見避けることができる一時的なポインタの使用を好む。効率の良いコンパイラを呪います!
この問題が発生しているすべてのプラットフォームで提供される低レベルのAPIがあります。私たちは、tmp変数の準備と、iOSとMac OS Xプラットフォーム用のOSMemoryBarrier()と呼ばれる_instance参照の割り当てとの間にメモリバリアを使用します。これは単に、バリアの前のコードを後続のコードとは独立して考慮する必要があるため、コンパイラのコード冗長性の解釈を排除するというコンパイラのインジケータとして機能します。ここで
がnilチェックで私たちの新しいコードです:ジョージによって
if (!_instance)
{
MySingleton* tmp = [[MySingleton alloc] init];
[tmp performAdditionalPrepWork];
OSMemoryBarrier();
_instance = tmp;
}
、私たちはそれを持っていると思います! 100%スレッドセーフなdouble-NULLチェックのシングルトンアクセサです。それは過労ですか?パフォーマンスとスレッドの安全性が重要かどうかによって異なります。ここでは、最終的なシングルトンの実装です:あなたはObjective-Cで行うには準備作業を持っていない場合
#include <libker/OSAtomic.h>
static MySingleton *_instance = nil;
+ (MySingleton *) instance
{
if (!_instance)
{
@synchronized (self)
{
if (!_instance)
{
MySingleton* tmp = [[MySingleton alloc] init];
[tmp performAdditionalPrepWork];
OSMemoryBarrier();
_instance = tmp;
}
}
}
return _instance;
}
は今、単にのalloc-inited MySingletonを割り当てることだけで結構です。しかし、C++では、一時的なメモリバリアブルバリアのトリックを行う必要があります。どうして?オブジェクトへの割り当てと参照への代入は、オブジェクトの構築前に行われるためです。あなたがC++を使用しない場合は
_instance = new MyCPPSingleton(someInitParam);
は用事、(効果的に)だから、
_instance = (MyCCPSingleton*)malloc(sizeof(MyCPPSingleton)); // allocating the memory
_instance->MyCPPSingleton(someInitParam); // calling the constructor
と同じですが、ない場合 - あなたはダブルの適用を予定している場合を念頭にそれを維持することを確認してくださいC++でNULLチェックのシングルトン。
[あなたのObjCシングルトンはどのように見えますか?](http://stackoverflow.com/questions/145154/what-does-your-objective-c-singleton-look-like) –