333 Stimmen

Wie sollte mein Objective-C-Singleton aussehen?

Meine Singleton-Accessor-Methode ist normalerweise eine Variante von:

static MyClass *gInstance = NULL;

+ (MyClass *)instance
{
    @synchronized(self)
    {
        if (gInstance == NULL)
            gInstance = [[self alloc] init];
    }

    return(gInstance);
}

Was könnte ich tun, um dies zu verbessern?

207voto

Robbie Hanson Punkte 3261

Eine weitere Möglichkeit ist die Verwendung der +(void)initialize Methode. Aus der Dokumentation:

Die Laufzeit sendet initialize an jede Klasse in einem Programm genau einmal, kurz bevor die Klasse oder jede Klasse, die von ihr erbt, ihre erste Nachricht innerhalb des Programms erhält. (Daher kann die Methode niemals aufgerufen werden, wenn die Klasse nicht verwendet wird.) Die Laufzeit sendet die initialize Nachricht an Klassen in einer thread-sicheren Weise. Superklassen erhalten diese Nachricht vor ihren Unterklassen.

Sie könnten also etwas Ähnliches wie das hier tun:

static MySingleton *sharedSingleton;

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        sharedSingleton = [[MySingleton alloc] init];
    }
}

95voto

Ben Hoffstein Punkte 99969
@interface MySingleton : NSObject
{
}

+ (MySingleton *)sharedSingleton;
@end

@implementation MySingleton

+ (MySingleton *)sharedSingleton
{
  static MySingleton *sharedSingleton;

  @synchronized(self)
  {
    if (!sharedSingleton)
      sharedSingleton = [[MySingleton alloc] init];

    return sharedSingleton;
  }
}

@end

[Quelle]

59voto

Colin Barrett Punkte 4441

Wie in meiner anderen Antwort unten beschrieben, sollten Sie das tun:

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

58voto

Louis Gerbarg Punkte 43196

Seit Kendall gebucht ein thread-sicheres Singleton, das versucht, Sperrkosten zu vermeiden, dachte ich, ich würde auch eine werfen:

#import <libkern/OSAtomic.h>

static void * volatile sharedInstance = nil;                                                

+ (className *) sharedInstance {                                                                    
  while (!sharedInstance) {                                                                          
    className *temp = [[self alloc] init];                                                                 
    if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) {
      [temp release];                                                                                   
    }                                                                                                    
  }                                                                                                        
  return sharedInstance;                                                                        
}

Okay, ich erkläre Ihnen, wie das funktioniert:

  1. Schneller Fall: Bei normaler Ausführung sharedInstance wurde bereits festgelegt, so dass die while Schleife wird nie ausgeführt und die Funktion kehrt zurück, nachdem sie lediglich das Vorhandensein der Variablen geprüft hat;

  2. Langsamer Fall: Wenn sharedInstance nicht existiert, wird eine Instanz zugewiesen und mit Hilfe von Compare And Swap ('CAS') dorthin kopiert;

  3. Angefochtener Fall: Wenn zwei Threads beide versuchen, die sharedInstance zur gleichen Zeit UND sharedInstance nicht zur gleichen Zeit existiert, werden beide neue Instanzen des Singletons initialisieren und versuchen, es in Position zu bringen. Wer das CAS gewinnt, kehrt sofort zurück, wer verliert, gibt die soeben zugewiesene Instanz wieder frei und gibt die (jetzt eingestellte) sharedInstance . Der einzelne OSAtomicCompareAndSwapPtrBarrier fungiert sowohl als Schreibsperre für den Einstell-Thread als auch als Lesesperre für den Test-Thread.

14voto

static MyClass \*sharedInst = nil;

+ (id)sharedInstance
{
    @synchronize( self ) {
        if ( sharedInst == nil ) {
            /\* sharedInst set up in init \*/
            \[\[self alloc\] init\];
        }
    }
    return sharedInst;
}

- (id)init
{
    if ( sharedInst != nil ) {
        \[NSException raise:NSInternalInconsistencyException
            format:@"\[%@ %@\] cannot be called; use +\[%@ %@\] instead"\],
            NSStringFromClass(\[self class\]), NSStringFromSelector(\_cmd), 
            NSStringFromClass(\[self class\]),
            NSStringFromSelector(@selector(sharedInstance)"\];
    } else if ( self = \[super init\] ) {
        sharedInst = self;
        /\* Whatever class specific here \*/
    }
    return sharedInst;
}

/\* These probably do nothing in
   a GC app.  Keeps singleton
   as an actual singleton in a
   non CG app
\*/
- (NSUInteger)retainCount
{
    return NSUIntegerMax;
}

- (oneway void)release
{
}

- (id)retain
{
    return sharedInst;
}

- (id)autorelease
{
    return sharedInst;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X