2011-09-01 5 views
16

Iがobj-Cでのシングルトンの素晴らしいリソースのカップルをお読みください。Objective-Cシングルトンがinitメソッドを実装する方法は?

  1. SO質問:What does your Objective-C singleton look like?
  2. 金曜日Q & A:Care and Feeding of Singletons
  3. アップルのドキュメント:Creating a Singleton Instance

が、どれもこれらのリソースのうちのinitメソッドの概念を明示的にと指定し、まだnov Obj-Cへの氷私はそれをどのように実装すべきか混乱している。

これまで私はを持っていることをObj-Cではできないことを知っています。実際のプライベートメソッドを提供していないので、[MyClass sharedInstance]ではなく[[MyClass alloc] init]に電話することができます。

私の他のオプションは何ですか?私は自分のシングルトンのシナリオをサブクラス化する必要もあると信じています。

答えて

25

まあ、initの簡単な方法は、デフォルトNSObject実装(これはselfを返す)を呼び出さないようにすることです。次に、sharedInstance関数の場合、シングルトンをインスタンス化するときにinitのような作業を実行するプライベート関数を定義して呼び出します。 (これにより、ユーザーは誤ってシングルトンを再初期化することはありません)。

ただし、大きな問題は、allocがあなたのコードのユーザによって呼び出されていることです!このために、私は個人的にallocWithZone:をオーバーライドするAppleのルートをお勧めします...

+ (id)allocWithZone:(NSZone *)zone 
{ 
    return [[self sharedInstance] retain]; 
} 

これは、ユーザーがまだあなたのシングルトンインスタンスを取得します、そして、彼らはそれを割り当てられたかのように、彼らは誤って使用することができ、かつ安全このため、一度それを解放意味しますカスタムallocはシングルトンにretainを実行します。 (注:allocallocWithZone:となり、別々に上書きする必要はありません)

私はあなたがより多くの情報が必要な場合は知ってみましょう〜

EDIT:例と詳細を提供するために、答えを拡大 - 、それはだ

を考慮にCatfish_Manの答えを撮影しばしば、防弾シングルトンを作成することが重要ではないと代わりにヘッダ/ドキュメントに分かりやすいコメントを書いてassertに入れてください。

私の場合、スレッドセーフな遅延ロードシングルトンが必要でした。つまり、アプリケーションの起動時に自動的に割り当てられる代わりに、使用するまで割り当てられません。それをどうやって安全に行うのかを学んだ後、私はそれをもっていっぱい行くかもしれないと思った。

EDIT#2:私は現在、アプリケーションの存続期間中にシングルトンオブジェクトを1回だけ割り当てるスレッドセーフな方法として、GCDのdispatch_once(...)を使用します。 Apple Docs: GCD dispatch_onceを参照してください。

//Hidden/Private initialization 
-(void)singletonInit 
{ 
    //your init code goes here 
} 

static HSCloudManager * sharedInstance = nil; 

+ (HSCloudManager *) sharedManager {         
    static dispatch_once_t dispatchOncePredicate = 0;     
    dispatch_once(&dispatchOncePredicate, ^{       
     sharedInstance = [[super allocWithZone:NULL] init];   
     [sharedInstance singletonInit];//Only place you should call singletonInit 
    });                 
    return sharedInstance;              
} 

+ (id) allocWithZone:(NSZone *)zone { 
    //If coder misunderstands this is a singleton, behave properly with 
    // ref count +1 on alloc anyway, and still return singleton! 
    return [[HSCloudManager sharedManager] retain]; 
} 

HSCloudManagerサブクラスNSObjectを、そして唯一のデフォルトを残しinitを上書きしません。また、私はまだ誤って複数回呼び出されてからそれを防ぐためにsingletonInitという名前Appleの古いシングルトンの例からallocWithZone:オーバーライドビットを追加し、民間のinitを追加しましたNSObjectに実装されています.Appleのドキュメントによれば、それは自己を返すだけです。これは[[HSCloudManager alloc] init][[[HSCloud Manager sharedManager] retain] self]と同じであることを意味し、混乱したユーザーとマルチスレッドアプリケーションの両方を遅延ロードシングルトンとして安全にします。

あなたのシングルトンをサブクラス化することについて懸念していますが、私はちょうどコメント/明確にそれを文書化すると言うでしょう。盲目的にクラスを読み上げることなくサブクラス化する人は誰でもです。痛みのためにを求めています!

EDIT#3:ARC互換について、ちょうどallocWithZone:オーバーライドから保持部分を除去するが、上書きを保ちます。

+0

@yAaak、十分な公正と思われますしかし、私は無限ループのように感じ、それを消化する必要があります;)プライベートメソッドによるこのようなシングルトンの作成プロセスがスレッドセーフであることを保証するにはどうすればよいですか?その他の問題:Appleの 'allocWithZone'のアイデアに従えば、デフォルトのNSObjectの' init'が呼び出されます(単に 'self'を返すかsth else?)...そして、ユーザーが' alloc'と ' init'は私のプロパティ/ ivarsの初期化と 'init'をもう一度取得するNSObjectについて何か変更しますか? – matm

+0

yAak、あなたは正しい:まあまあのアプローチは、今のところ十分なことを示唆している。あなたのコードを使ってシングルトンをもう少し試してみましょう:)あなたの答えはかなり広範で、Catfish_manの反対意見を取り入れているので、私はそれを受け入れています。再度、感謝します! – matm

+0

OSAtomic *ではなくdispatch_once()をお勧めします。より速く、より速く、より厄介ではありません。 –

2

initメソッドには影響しません。シングルトンクラスでも通常のクラスと同じです。クラスのインスタンスを複数作成しないようにするには、(allocが呼び出されます)を上書きすることができます。

3

正直?防弾シングルトンのクラスを書くことの全体の流行は、私にはあまりにも大声ではないようです。真剣に心配しているのであれば、最初にアサーションを割り当てる前にassert(sharedInstance == nil)をそこに貼り付けてください。そうすれば、誰かが間違った使い方をするとクラッシュし、速やかに彼らがばかだと知らせることができます。

+2

部分的に私は同意するが、私の他の半分は言う:なぜ妥協?しかし、私はヘッダー/ドキュメントの明確なコメントに加えて、私が達成できるものに行くつもりです。 – matm

-1

これは

+(id)allocWithZone:(struct _NSZone *)zone{ 
    static dispatch_once_t onceToken_alloc; 
    static id __alloc_instance; 
    dispatch_once(&onceToken_alloc, ^{ 
     __alloc_instance = [super allocWithZone:zone]; 
    }); 
    return __alloc_instance; 
} 

-(id)init{ 
    static id __instance; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     __instance = [super init]; 
    }); 
    return __instance; 
} 
+0

これは、シングルトンインスタンスを返すのではなく、新しいインスタンスを割り当てています。 – ebi

+0

いいえ、そうではありません。 – dotAramis

+0

それを試してみて、あなたは正しいですが、シングルトンは、インスタンスを取得するためにコンシューマコールのallocを必要とするのではなく、アクセスのためのクラスメソッドを実装する必要があります。 – ebi

0

あなたのヘッダファイルにNS_UNAVAILABLEマクロを使用することができ、あなたのシングルトンクラスの発信者のためのinit /新しいメソッドが利用できないようにしよう:

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

+ (instancetype)sharedInstance; 
関連する問題