Objective-CでGCDのdispatch_onceを使用してシングルトンを作成する


341

iOS 4.0以上をターゲットにできる場合

GCDを使用して、Objective-C(スレッドセーフ)でシングルトンを作成する最良の方法ですか?

+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

2
クラスのユーザーがalloc / copyを呼び出せないようにする方法はありますか?
Nicolas Miari

3
dispatch_once_tとdispatch_onceは、4.1ではなく4.0で導入されたようです(参照:developer.apple.com/library/ios/#documentation/Performance/…
Ben Flynn

1
initがシングルトンオブジェクトの使用を必要とする場合、このメソッドは問題になります。Matt Gallagherのコードは、数回以上私に役立ちました。 cocoawithlove.com/2008/11/...
グレッグ

1
この例ではそれは取るに足らないことを知っています。しかし、なぜ人々はもっと「新しい」を使わないのですか。dispatch_once(&一度、^ {sharedInstance = [自己新を];}。。ちょうどそのビットすっきり見えることがALLOC +のinitと同等だ
クリス・ハットン

3
必ず戻り値の型の使用を開始してくださいinstancetype。コードの補完は、の代わりに使用する方がはるかに優れていますid
ロジャース氏2013

回答:


215

これは、クラスのインスタンスを作成するための完全に受け入れ可能なスレッドセーフな方法です。技術的には「シングルトン」ではない(これらのオブジェクトは1つしか存在できないため)が、[Foo sharedFoo]メソッドを使用してオブジェクトにアクセスするだけであれば、これで十分です。


4
どうやってリリースするの?
samvermette 2012年

65
@samvermetteしません。シングルトンのポイントは、それが常に存在することです。したがって、それを解放せず、メモリはプロセスの終了時に再利用されます。
Dave DeLong 2012年

6
@Dave DeLong:私の考えでは、シングルトンを持つことの目的は、その不死性の確実性ではなく、1つのインスタンスを持つ確実性です。そのシングルトンがセマフォをデクリメントするとどうなりますか?それが常に存在するとは言い切れません。
jacekmigacz 2013年

4
@hooleyhoopはい、そのドキュメントで。「複数のスレッドから同時に呼び出された場合、この関数はブロックが完了するまで同期的に待機します。」
ケビン

3
@ WalterMartinVargas-Pena強い参照は静的変数によって保持されます
Dave DeLong

36

インスタンスタイプ

instancetypeはの多くの言語拡張機能の1つにすぎずObjective-C、新しいリリースごとに追加されます。

知って、愛して。

そして、低レベルの詳細に注意を払うことで、Objective-Cを変革する強力な新しい方法を洞察する方法を例にとります。

ここを参照してください:instancetype


+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}

+ (Class*)sharedInstance
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}

4
素晴らしいヒント、ありがとう! instancetypeは、メソッドが関連する結果タイプを返すことを通知する結果タイプとして使用できるコンテキストキーワードです。... instancetypeを使用すると、コンパイラはタイプを正しく推測します。
Fattie 2013年

1
ここで2つのスニペットが何を意味するのかははっきりしませんが、それらは互いに同等ですか?一方が他方よりも望ましいですか?作者がこれについて少し説明を追加できるといいですね。
ギャラクティカ

33

MySingleton.h

@interface MySingleton : NSObject

+(instancetype)sharedInstance;

+(instancetype)alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype)init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype)new __attribute__((unavailable("new not available, call sharedInstance instead")));
-(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead")));

@end

MySingleton.m

@implementation MySingleton

+(instancetype)sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype)initUniqueInstance {
    return [super init];
}

@end

initを利用できないのはなぜですか?少なくとも1つは利用できませんinitか?
ハニー

2
シングルトンのアクセスポイントは1つだけにする必要があります。そして、この点がsharedInstanceです。* .hファイルにinitメソッドがある場合は、別のシングルトンインスタンスを作成できます。これは、シングルトンの定義と矛盾します。
Sergey Petruk

1
@ asma22 __attribute __((unavailable())は、これらのメソッドを使用できないようにします。他のプログラマーがメソッドを使用不可としてマーク付けしたい場合、エラーになります
Sergey Petruk

1
私は完全に理解し、私は何か新しいことを学んだことを嬉しく思います。あなたの答えには何の問題もありません。初心者には少し混乱するかもしれません...
ハニー

1
これはMySingleton、たとえば、MySingleton.m私が電話している場合にのみ機能します[super alloc]
Sergey Petruk

6

allocメソッドを上書きすると、クラスが割り当てられることを回避できます。

@implementation MyClass

static BOOL useinside = NO;
static id _sharedObject = nil;


+(id) alloc {
    if (!useinside) {
        @throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil];
    }
    else {
        return [super alloc];
    }
}

+(id)sharedInstance
{
    static dispatch_once_t p = 0;
    dispatch_once(&p, ^{
        useinside = YES;
        _sharedObject = [[MyClass alloc] init];
        useinside = NO;
    });   
    // returns the same object each time
    return _sharedObject;
}

1
これは上のコメントで私の質問に答えます。私はディフェンシブプログラミングが大好きなわけではありませんが...
Nicolas Miari

5

デイブは正しいです、それは完全に素晴らしいです。シングルトンの作成に関するAppleのドキュメントをチェックして、クラスがsharedFooメソッドを使用しないことを選択した場合に1つしか作成できないようにするための他のメソッドの実装に関するヒントを確認することをお勧めします。


8
ええと...それはシングルトンを作成する最高の例ではありません。メモリ管理メソッドをオーバーライドする必要はありません。
Dave DeLong 2011

19
これは、ARCの使用では完全に無効です。
logancautrell

引用された文書は廃止されました。また、外部コンテンツへのリンクのみである回答は、一般的に貧弱なSO回答です。回答の中の少なくとも関連する部分を抜粋してください。後世のために保存された古い方法が必要でない限り、ここで気にしないでください。
toolbear、2015

4

[[MyClass alloc] init]がsharedInstanceと同じオブジェクトを返すことを確認したい場合(私の意見では不要ですが、一部の人々はそれを望んでいます)、2番目のdispatch_onceを使用して非常に簡単かつ安全に行うことができます。

- (instancetype)init
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        // Your normal init code goes here. 
        sharedInstance = self;
    });

    return sharedInstance;
}

これにより、[[MyClass alloc] init]と[MyClass sharedInstance]の任意の組み合わせで同じオブジェクトを返すことができます。[MyClass sharedInstance]はもう少し効率的です。仕組み:[MyClass sharedInstance]は[[MyClass alloc] init]を1回呼び出します。他のコードでも何度でも呼び出すことができます。initの最初の呼び出し元は、「通常の」初期化を行い、シングルトンオブジェクトをinitメソッドに格納します。それ以降のinitの呼び出しは、返されたallocを完全に無視し、同じsharedInstanceを返します。allocの結果は割り当て解除されます。

+ sharedInstanceメソッドは、従来どおり機能します。[[MyClass alloc] init]を呼び出す最初の呼び出し元ではない場合、initの結果はalloc呼び出しの結果ではありませんが、問題ありません。


2

これが「シングルトンを作成する最良の方法」であるかどうかを尋ねます。

いくつかの考え:

  1. まず、はい、これはスレッドセーフなソリューションです。このdispatch_onceパターンは、Objective-Cでシングルトンを生成するための最新のスレッドセーフな方法です。そこに心配はありません。

  2. しかし、これが「最良の」方法かどうかを尋ねました。一つは、しかし、認めるべきであるinstancetype[[self alloc] init]シングルトンと組み合わせて使用する場合、潜在的に誤解を招くです。

    の利点はinstancetype、クラスの型に頼らずにクラスをサブクラス化できることを明確に宣言できることです。idようにのです。

    しかし、staticこのメソッドにはサブクラス化の課題があります。ImageCacheBlobCacheシングルトンが両方ともCache、独自のsharedCacheメソッドを実装せずにスーパークラスのサブクラスである場合はどうなりますか?

    ImageCache *imageCache = [ImageCache sharedCache];  // fine
    BlobCache *blobCache = [BlobCache sharedCache];     // error; this will return the aforementioned ImageCache!!!

    これを機能させるには、サブクラスが独自のクラスを実装していることを確認する必要があります sharedInstance(または特定のクラスのために呼び出すもの)メソッドをがあります。

    結論、あなたのオリジナルのsharedInstance ルックスサブクラスをサポートするようにが、サポートしません。サブクラス化をサポートする場合は、少なくとも、将来の開発者にこのメソッドをオーバーライドする必要があることを警告するドキュメントを含めてください。

  3. Swiftとの最適な相互運用性のために、おそらくこれをクラスメソッドではなくプロパティとして定義する必要があります。例:

    @interface Foo : NSObject
    @property (class, readonly, strong) Foo *sharedFoo;
    @end

    次に、このプロパティのゲッターを作成します(実装では、dispatch_once提案したパターンを使用します)。

    + (Foo *)sharedFoo { ... }

    これの利点は、Swiftユーザーが使用する場合、次のようなことを行うことです。

    let foo = Foo.shared

    注、ありません ()プロパティとして実装したため、。Swift 3以降、これがシングルトンへの一般的なアクセス方法です。したがって、それをプロパティとして定義すると、その相互運用性が促進されます。

    余談ですが、Appleがシングルトンをどのように定義しているかを見ると、これは彼らが採用したパターンです。たとえば、NSURLSessionシングルトンは次のように定義されています。

    @property (class, readonly, strong) NSURLSession *sharedSession;
  4. もう1つの非常にマイナーなSwiftの相互運用性に関する考慮事項は、シングルトンの名前でした。タイプではなくタイプの名前を組み込むことができるのが最善ですsharedInstance。たとえば、クラスがの場合Foo、シングルトンプロパティをとして定義できますsharedFoo。または、クラスがあった場合、DatabaseManagerプロパティを呼び出すことができますsharedManager。次に、Swiftユーザーは次のことができます。

    let foo = Foo.shared
    let manager = DatabaseManager.shared

    明らかに、本当にを使用したい場合は、次のようにしたいsharedInstance場合は常にSwift名を宣言できます。

    @property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);

    明らかに、Objective-Cコードを作成するときは、Swiftの相互運用性が他の設計上の考慮事項を上回らないようにする必要があります。

  5. これを、開発者が自分のインスタンスをインスタンス化できない(偶然に)インスタンス化できない真のシングルトンにしたい場合、unavailable修飾子はオンinitnewあり、賢明であると指摘する他の人にも同意します。


0

スレッドセーフなシングルトンを作成するには、次のようにします。

@interface SomeManager : NSObject
+ (id)sharedManager;
@end

/* thread safe */
@implementation SomeManager

static id sharedManager = nil;

+ (void)initialize {
    if (self == [SomeManager class]) {
        sharedManager = [[self alloc] init];
    }
}

+ (id)sharedManager {
    return sharedManager;
}
@end

このブログでは、objc / cocoaのシングルトンを非常によく説明しています


あなたは非常に古い記事にリンクしていますが、OPは最新の実装に関する特性を求めています。
vikingosegundo 2014

1
問題は特定の実装についてです。別の実装を投稿するだけです。そのため、あなたは質問に答えようとさえしません。
vikingosegundo 2014

1
@vikingosegundo質問者は、GCDがスレッドセーフなシングルトンを作成する最良の方法であるという天気を尋ねますが、私の答えは別の選択肢を与えます。
Hancock_Xu 14

質問者は、特定の実装がスレッドセーフかどうかを尋ねます。彼はオプションを求めていません。
vikingosegundo 2015年

0
//Create Singleton  
  +( instancetype )defaultDBManager
    {

        static dispatch_once_t onceToken = 0;
        __strong static id _sharedObject = nil;

        dispatch_once(&onceToken, ^{
            _sharedObject = [[self alloc] init];
        });

        return _sharedObject;
    }


//In it method
-(instancetype)init
{
    self = [super init];
  if(self)
     {
   //Do your custom initialization
     }
     return self;
}

0
@interface className : NSObject{
+(className*)SingleTonShare;
}

@implementation className

+(className*)SingleTonShare{

static className* sharedObj = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{

if (sharedObj == nil){
    sharedObj = [[className alloc] init];
}
  });
     return sharedObj;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.