1

スレッドセーフでARC互換のシングルトンが欲しいのですが、私が見つけたシングルトンの最も一般的な例、ここに貼り付けた例のようです。

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

他の開発者が[[MyClassalloc]init]を呼び出して、目的のフローをオーバーライドするのを止めません。それを処理する適切な方法は何ですか(initで例外をスローすることは別として)?

4

4 に答える 4

2

シングルトンパターンの代わりにボーグパターンを使用します。クラスの複数のインスタンス化を許可し、インスタンスに同じ静的状態を共有させます。

// Shared data
static NSDictionary *sharedData = nil;

+ (void) initialize {
  // Initialize shared data
  sharedData = [[NSDictionary alloc] init];
}

- (id) init {
  self = [super init];

  if (self) {
    self.data = sharedData;
  }
}

getInstanceこのように、クライアントは静的メソッドまたはメソッドを任意に使用initし、同じ状態を共有するオブジェクトを受け取ることができます。彼らはそれがシングルトンであることを意識する必要さえありません。

于 2013-02-01T13:03:43.940 に答える
2

+allocまた、シングルトンの複数のインスタンスを割り当てないように、メソッドをオーバーライドする必要があります。

編集#3:ええと 、私は公式ドキュメントがメソッドのオーバーライドについて何を言っているかを本当に知っています +alloc が、求められた利益を達成するためにそれを回避する方法はありません。個人的に私はそれをすることに同意しませんが、それは望ましい結果を提供することができます。

次のようになります。

static MyClass *_sharedInstance = nil;
static BOOL _bypassAllocMethod = TRUE;

+ (id)sharedInstance {
    @synchronized([MyClass class]) {
         if (_sharedInstance == nil) {
              _sharedInstance = [[MyClass alloc] init];
         }
    }
    return _sharedInstance;
}

+ (id)alloc {
    @synchronized([MyClass class]) {
         _bypassAllocMethod = FALSE; // EDIT #2
         if (_sharedInstance == nil) {
              _sharedInstance = [super alloc];
              return _sharedInstance;
         } else {
              // EDIT #1 : you could throw an exception here to avoid the double allocation of the singleton class
              @throw [NSException exceptionWithName:[NSString stringWithFormat:@"<%@: %p> Double allocation issue", [_sharedInstance class], _sharedInstance] reason:@"You cannot allocate the singeton class twice or more." userInfo:nil];
         }
    }
    return nil;
}

// EDIT #2 : the init method
- (id)init {
    if (_bypassAllocMethod)
        @throw [NSException exceptionWithName:@"invalid allocation" reason:@"invalid allocation" userInfo:nil];

    if (self = [super init]) {
    }

    return self
}

編集#1

ここで例外をスローする必要はありませんが、単純なnilポインターを返すよりも、開発者がクラスを間違った方法で使用する方が視覚的なフィードバックになります。

編集#2

開発者がクラスをインスタンス化して変更されたメソッドをバイパスしないようにするための簡単なトリックを追加しました+alloc。その場合、割り当ては正常に機能しますが-init、例外がスローされます。

于 2013-02-01T13:05:45.967 に答える
1

私は以下を使用する傾向があります:(新しいinstancetypeコンパイラ構文で)

@implementation MyClass

+ (instancetype)myClass {
  static MyClass *singleton; // keep global variables in the most minimal scope

  if (singleton == nil) @synchronized (self) {
    singleton = [[MyClass alloc] initPrivate];
  }

  return singleton;
}

- (instancetype)initPrivate { // ARC requires the method start with "init…"
  self = [super init];

  return self;
}

- (instancetype)init {
  return nil;
}

@end

これはまた、他の人が電話をかけるのを妨げることはありません[[MyClass alloc] privateInit]が、警告します(彼らが独自のハッシュコードを書かない限り)。

これは、誰かが電話をかけ[[MyClass alloc] init]た場合にもARC以前のリークになりますが、それが発生した場合はさらに大きな問題が発生します。オプションで、initが呼び出されたときに例外をスローできます。(holexの答えのように)

また、サブクラスは理論的には競合状態になる可能性があります。@synchronized (self)その変更が心配な場合は@synchronized ([McClass class])。私はよりクリーンなコードを好み、selfサブクラスがないことを知っています。

于 2013-09-26T20:23:28.993 に答える
0

インスタンスをクラスメソッドに変換し、Classオブジェクトをシングルトンとして使用します。

たとえば、このようなシングルトンクラスがあります

@interface MySingleton {
    int count;
}

+ (MySingleton *)sharedInstance;
- (int)getNext;

@end

私はあなたにそれをに変換することを提案しています

@interface MySingleton

+ (int)getNext;

@end

MySingleton.mで

static int count;

その後、あなたはそれを次のように使うことができます

[MySingleton getNext];

また

id obj = [MySingleton class]; // Class objects are singleton provided by runtime
[obj getNext];

編集

指摘したいのですが、シングルトンパターン用のObjCの実装はすでにたくさんあります。

http://www.cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html https://github.com/fadingred/objc-singleton

単純なグーグル検索はそれらを見つけるでしょう。すべてが考慮されます。(私が予想していたよりもはるかに)

于 2013-02-01T13:06:11.757 に答える