web-dev-qa-db-ja.com

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

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;
}
337
Ryan

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

212
Dave DeLong

インスタンスタイプ

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

知って、愛して。

また、低レベルの詳細に注意を払うと、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;
}
35
Zelko

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
33
Sergey Petruk

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;
}
6
i-developer

デイブは正しいです、それはまったく問題ありません。 シングルトンの作成に関するAppleのドキュメント 他のメソッドを実装するためのヒントについては、クラスがsharedFooメソッドを使用しないことを選択した場合に1つのみを作成できるようにすることをお勧めします。

5
Christian

[[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呼び出しの結果ではありませんが、それで問題ありません。

4
gnasher729

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

いくつかの考え:

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

  2. しかし、あなたはこれがそれを行うための「最良の」方法であるかどうか尋ねました。ただし、instancetypeおよび[[self alloc] init]はシングルトンと組み合わせて使用​​すると誤解を招く可能性があることを認識する必要があります。

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

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

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

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

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

  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修飾子が賢明であると指摘する他の人に同意します。

1
Rob

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

@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のシングルトン

0
Hancock_Xu
//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
Rohit Kashyap