web-dev-qa-db-ja.com

ARCと互換性のあるObjective-Cシングルトンを実装するにはどうすればよいですか?

Xcode 4.2で自動参照カウント(ARC)を使用しているときにコンパイルおよび正常に動作するシングルトンクラスを変換(または作成)するにはどうすればよいですか?

168
cescofry

あなたが既にそれをしているはずであるのとまったく同じ方法で:

+ (instancetype)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}
377
Nick Forge

必要に応じて他のインスタンスを作成する場合は、次のようにします。

+ (MyClass *)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

それ以外の場合は、これを行う必要があります。

+ (id)allocWithZone:(NSZone *)zone
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}
8
DongXu

これはARCおよび非ARCのバージョンです

使い方:

MySingletonClass.h

@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end

MySingletonClass.m

#import "MySingletonClass.h"
#import "SynthesizeSingleton.h"
@implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
@end
5
Igor

これはARCでの私のパターンです。 GCDを使用して新しいパターンを満たし、Appleの古いインスタンス生成防止パターンも満たします。

@implementation AAA
+ (id)alloc
{
    return  [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
    [self doesNotRecognizeSelector:_cmd];
    abort();
}
+ (instancetype)theController
{
    static AAA* c1  =   nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        c1  =   [[super allocWithZone:nil] init];

        // For confirm...       
        NSLog(@"%@", NSStringFromClass([c1 class]));    //  Prints AAA
        NSLog(@"%@", @([c1 class] == self));            //  Prints 1

        Class   real_superclass_obj =   class_getSuperclass(self);
        NSLog(@"%@", @(real_superclass_obj == self));   //  Prints 0
    });

    return  c1;
}
@end
2
Eonil

この答えを読んでから、他の答えを読んでください。

ソリューションを理解できない場合、シングルトンが何を意味するのか、そしてその要件が何なのかを最初に知る必要があります。

シングルトンを正常に作成するには、次の3つを実行できる必要があります。

  • 競合状態 があった場合、SharedInstanceの複数のインスタンスを同時に作成することはできません!
  • 複数の呼び出し間で値を覚えておいてください。
  • 一度だけ作成します。エントリポイントを制御する。

dispatch_once_tは、ブロックを1回だけディスパッチできるようにすることで、競合状態を解決するのに役立ちます。

Static は、任意の数の呼び出しでその値を「記憶」するのに役立ちます。どのように覚えていますか? sharedInstanceの正確な名前を持つ新しいインスタンスを再度作成することはできません。元々作成されたインスタンスでのみ機能します。

sharedInstanceクラスでは、allocinitを使用していない(つまり、NSObjectサブクラスなので、使用しないでくださいが、allocinitメソッドがまだあります) +(instancetype)sharedInstanceを使用してこれを実現します。これは、同時に異なるスレッドからの複数の試行に関係なく、initiated onceに限定され、その値を記憶します。

Cocoaに付属している最も一般的なシステムシングルトンの一部は次のとおりです。

  • [UIApplication sharedApplication]
  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • [NSOperations mainQueue]
  • [NSNotificationCenter defaultCenter]

基本的に、集中化された効果が必要なものはすべて、何らかのシングルトン設計パターンに従う必要があります。

2
Honey

あるいは、Objective-CはNSObjectおよびそのすべてのサブクラスに+(void)initializeメソッドを提供します。クラスのメソッドの前に常に呼び出されます。

IOS 6でブレークポイントを1つに一度設定すると、stack_onceがスタックフレームに表示されました。

1
Walt Sellers

シングルトンクラス:どのような場合でも、どのような方法でも、クラスのオブジェクトを複数作成することはできません。

+ (instancetype)sharedInstance
{
    static ClassName *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[ClassName alloc] init];
        // Perform other initialisation...
    });
    return sharedInstance;
}
//    You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only. 

-(MyClass)init
{
   return [ClassName sharedInstance];
}
0
Yogi

受け入れられた回答には2つの問題がありますが、それはあなたの目的に関連する場合と関連しない場合があります。

  1. Initメソッドから、何らかの方法でsharedInstanceメソッドが再度呼び出されると(たとえば、シングルトンを使用する他のオブジェクトがそこから構築されるため)、スタックオーバーフローが発生します。
  2. クラス階層の場合、階層内の具象クラスごとに1つのシングルトンではなく、1つのシングルトン(つまり、sharedInstanceメソッドが呼び出された階層内の最初のクラス)のみがあります。

次のコードは、これらの問題の両方を処理します。

+ (instancetype)sharedInstance {
    static id mutex = nil;
    static NSMutableDictionary *instances = nil;

    //Initialize the mutex and instances dictionary in a thread safe manner
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mutex = [NSObject new];
        instances = [NSMutableDictionary new];
    });

    id instance = nil;

    //Now synchronize on the mutex
    //Note: do not synchronize on self, since self may differ depending on which class this method is called on
    @synchronized(mutex) {
        id <NSCopying> key = (id <NSCopying>)self;
        instance = instances[key];
        if (instance == nil) {
            //Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
            id allocatedInstance = [self alloc];

            //Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
            //Do this right after allocation to avoid the stackoverflow problem
            if (allocatedInstance != nil) {
                instances[key] = allocatedInstance;
            }
            instance = [allocatedInstance init];

            //Following code may be overly cautious
            if (instance != allocatedInstance) {
                //Somehow the init method did not return the same instance as the alloc method
                if (instance == nil) {
                    //If init returns nil: immediately remove the instance again
                    [instances removeObjectForKey:key];
                } else {
                    //Else: put the instance in the dictionary instead of the allocatedInstance
                    instances[key] = instance;
                }
            }
        }
    }
    return instance;
}
0