Frage

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?

War es hilfreich?

Lösung

Eine andere Möglichkeit besteht darin, das zu verwenden +(void)initialize Methode.Aus der Dokumentation:

Die Laufzeit sendet initialize an jede Klasse in einem Programm genau einmal, kurz bevor die Klasse oder eine Klasse, die von ihr erbt, ihre erste Nachricht aus dem Programm gesendet wird.(Daher darf die Methode niemals aufgerufen werden, wenn die Klasse nicht verwendet wird.) Die Laufzeit sendet die initialize Nachricht an Klassen auf threadsichere Weise senden.Oberklassen erhalten diese Nachricht vor ihren Unterklassen.

Sie könnten also etwa Folgendes tun:

static MySingleton *sharedSingleton;

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

Andere Tipps

@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]

Per meine andere Antwort unten, ich glaube, Sie tun sollten:

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

Da Kendall geschrieben eine THREAD Singletons, die Kosten zu vermeiden versucht, Sperren, ich dachte, dass ich ein, so gut werfen würde:

#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, lassen Sie mich erklären, wie das funktioniert:

  1. Fast Fall: Im Normal hat Ausführung sharedInstance bereits eingestellt worden ist, so dass die while Schleife nie ausgeführt und die Funktion kehrt nach nur für die Variable Existenz zu testen;

  2. Langsam Fall: Wenn sharedInstance nicht existiert, dann wird eine Instanz zugeordnet und in sie kopiert mit einer Vergleichs- und Auslagerungs ( 'CAS');

  3. stritten Fall: Wenn zwei Threads versuchen beide sharedInstance zugleich zu nennen und sharedInstance nicht zur gleichen Zeit vorhanden ist, dann wird sie neue Instanzen der Singleton initialisieren beide und Versuch es in der richtigen Position zu CAS. Unabhängig davon, welche man gewinnt das CAS sofort zurückkehrt, je nachdem, was man verliert Releases die Instanz es nur zugewiesen und gibt die (jetzt eingestellt) sharedInstance. Die einzelne OSAtomicCompareAndSwapPtrBarrier wirken sowohl als Schreibbarriere für die Einstellung Gewinde und eine Lesesperre von den Testgewinden.

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

Edit: Diese Implementierung mit ARC veraltete. Weitere Informationen finden sie unter Wie kann ich einen Objective-C Singleton implementieren, die mit ARC kompatibel ist? für die korrekte Umsetzung .

Alle Implementierungen von initialize ich in anderen Antworten gelesen habe einen gemeinsamen Fehler.

+ (void) initialize {
  _instance = [[MySingletonClass alloc] init] // <----- Wrong!
}

+ (void) initialize {
  if (self == [MySingletonClass class]){ // <----- Correct!
      _instance = [[MySingletonClass alloc] init] 
  }
}

Die Apple-Dokumentation wird empfohlen, den Klassentyp in Ihrem initialize Block zu überprüfen. Da Subklassen rufen Sie die initialize standardmäßig. Es gibt einen nicht-offensichtlichen Fall, in dem Subklassen kann indirekt durch KVO erstellt werden. Denn wenn Sie die folgende Zeile in einer anderen Klasse hinzufügen:

[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]

Objective-C wird implizit eine Unterklasse von MySingletonClass schafft in einer zweiten Auslösung +initialize zur Folge hat.

Sie können denken, dass Sie implizit für doppelte Initialisierung im Init-Block als solche überprüfen sollten:

- (id) init { <----- Wrong!
   if (_instance != nil) {
      // Some hack
   }
   else {
      // Do stuff
   }
  return self;
}

Aber Sie werden sich in den Fuß schießen; oder noch schlimmer ein anderer Entwickler die Möglichkeit geben, sich selbst in den Fuß zu schießen.

- (id) init { <----- Correct!
   NSAssert(_instance == nil, @"Duplication initialization of singleton");
   self = [super init];
   if (self){
      // Do stuff
   }
   return self;
}

TL; DR, hier ist meine Implementierung

@implementation MySingletonClass
static MySingletonClass * _instance;
+ (void) initialize {
   if (self == [MySingletonClass class]){
      _instance = [[MySingletonClass alloc] init];
   }
}

- (id) init {
   ZAssert (_instance == nil, @"Duplication initialization of singleton");
   self = [super init];
   if (self) {
      // Initialization
   }
   return self;
}

+ (id) getInstance {
   return _instance;
}
@end

(Ersetzen ZAssert mit unserer eigenen Behauptung Makro oder nur NSAssert.)

Eine ausführliche Erklärung des Singleton-Makrocode ist auf dem Blog Cocoa With Love

http://cocoawithlove.com/2008/11 /singletons-appdelegates-and-top-level.html .

Ich habe eine interessante Variante sharedInstance, den Thread-sicher ist, aber nicht gesperrt nach der Initialisierung. Ich bin mir noch nicht sicher genug, um von der Top-Antwort zu ändern, wie gewünscht, aber ich präsentiere es für die weitere Diskussion:

// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;

// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
    return (MySingleton *)sharedInstance;
}

+ (MySingleton*)sharedInstance
{
    @synchronized(self)
    {
        if (sharedInstance == nil)
        {
            sharedInstance = [[MySingleton alloc] init];
            // Replace expensive thread-safe method 
            // with the simpler one that just returns the allocated instance.
            SEL origSel = @selector(sharedInstance);
            SEL newSel = @selector(simpleSharedInstance);
            Method origMethod = class_getClassMethod(self, origSel);
            Method newMethod = class_getClassMethod(self, newSel);
            method_exchangeImplementations(origMethod, newMethod);
        }
    }
    return (MySingleton *)sharedInstance;
}

Kurze Antwort:. Fabulous

Lange Antwort: So etwas ....

static SomeSingleton *instance = NULL;

@implementation SomeSingleton

+ (id) instance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == NULL){
            instance = [[super allocWithZone:NULL] init];
        }
    });
    return instance;
}

+ (id) allocWithZone:(NSZone *)paramZone {
    return [[self instance] retain];
}

- (id) copyWithZone:(NSZone *)paramZone {
    return self;
}

- (id) autorelease {
    return self;
}

- (NSUInteger) retainCount {
    return NSUIntegerMax;
}

- (id) retain {
    return self;
}

@end

Seien Sie sicher, dass der Versand lesen / once.h Header verstehen Was ist los. In diesem Fall sind die Header Kommentare mehr anwendbar ist als die docs oder man-Seite.

Ich habe Singleton in eine Klasse gerollt, so dass andere Klassen Singleton Eigenschaften erben können.

Singleton.h:

static id sharedInstance = nil;

#define DEFINE_SHARED_INSTANCE + (id) sharedInstance {  return [self sharedInstance:&sharedInstance]; } \
                               + (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; }

@interface Singleton : NSObject {

}

+ (id) sharedInstance;
+ (id) sharedInstance:(id*)inst;

+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst;

@end

Singleton.m:

#import "Singleton.h"


@implementation Singleton


+ (id) sharedInstance { 
    return [self sharedInstance:&sharedInstance];
}

+ (id) sharedInstance:(id*)inst {
    @synchronized(self)
    {
        if (*inst == nil)
            *inst = [[self alloc] init];
    }
    return *inst;
}

+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst {
    @synchronized(self) {
        if (*inst == nil) {
            *inst = [super allocWithZone:zone];
            return *inst;  // assignment and return on first allocation
        }
    }
    return nil; // on subsequent allocation attempts return nil
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
    //do nothing
}

- (id)autorelease {
    return self;
}


@end

Und hier ist ein Beispiel einer Klasse, dass Sie Singletons werden wollen.

#import "Singleton.h"

@interface SomeClass : Singleton {

}

@end

@implementation SomeClass 

DEFINE_SHARED_INSTANCE;

@end

Die einzige Einschränkung über Singleton Klasse, ist, dass es NSObject Unterklasse ist. Aber die meiste Zeit ich Singletons in meinem Code verwenden, sie sind in der Tat NSObject Unterklassen, so dass diese Klasse wirklich mein Leben erleichtern und Code sauberer machen.

Dies funktioniert in einer nicht-Müll gesammelt Umgebung auch.

@interface MySingleton : NSObject {
}

+(MySingleton *)sharedManager;

@end


@implementation MySingleton

static MySingleton *sharedMySingleton = nil;

+(MySingleton*)sharedManager {
    @synchronized(self) {
        if (sharedMySingleton == nil) {
            [[self alloc] init]; // assignment not done here
        }
    }
    return sharedMySingleton;
}


+(id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (sharedMySingleton == nil) {
            sharedMySingleton = [super allocWithZone:zone];
            return sharedMySingleton;  // assignment and return on first allocation
        }
    }
    return nil; //on subsequent allocation attempts return nil
}


-(void)dealloc {
    [super dealloc];
}

-(id)copyWithZone:(NSZone *)zone {
    return self;
}


-(id)retain {
    return self;
}


-(unsigned)retainCount {
    return UINT_MAX;  //denotes an object that cannot be release
}


-(void)release {
    //do nothing    
}


-(id)autorelease {
    return self;    
}


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

    //initialize here

    return self;
}

@end

Sollten diese werden nicht Thread-sicher und vermeiden die teure Verriegelung nach dem ersten Aufruf?

+ (MySingleton*)sharedInstance
{
    if (sharedInstance == nil) {
        @synchronized(self) {
            if (sharedInstance == nil) {
                sharedInstance = [[MySingleton alloc] init];
            }
        }
    }
    return (MySingleton *)sharedInstance;
}

Wie wäre es

static MyClass *gInstance = NULL;

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

    return(gInstance);
}

So können Sie die Synchronisation Kosten nach der Initialisierung vermeiden?

Für eine eingehende Diskussion des Singletonmuster in Objective-C, schau mal hier:

Mit dem Singleton-Muster in Objective-C

  

KLSingleton ist:

     
      
  1. Subclassible (zum n-ten Grades)
  2.   
  3. ARC kompatibel
  4.   
  5. Sicher mit alloc und init
  6.   
  7. Loaded lazily
  8.   
  9. Thread-safe
  10.   
  11. Sperren frei (Anwendungen + initialisieren, nicht @synchronize)
  12.   
  13. Makro-frei
  14.   
  15. Swizzle frei
  16.   
  17. Einfache
  18.   

KLSingleton

Sie wollen nicht auf sich selbst synchronisieren ... Da das Selbst Objekt existiert noch nicht! Sie enden auf einem temporären ID-Wert nach oben zu verriegeln. Sie wollen sicherstellen, dass niemand sonst Klassenmethoden ausführen können (sharedInstance, alloc, allocWithZone :, etc.), so müssen Sie auf das Klassenobjekt synchronisieren statt:

@implementation MYSingleton

static MYSingleton * sharedInstance = nil;

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

    return sharedInstance;
}

+( id )allocWithZone:( NSZone * )zone {
    @synchronized( [ MYSingleton class ] ) {
        if( sharedInstance == nil )
            sharedInstance = [ super allocWithZone:zone ];
    }

    return sharedInstance;
}

-( id )init {
    @synchronized( [ MYSingleton class ] ) {
        self = [ super init ];
        if( self != nil ) {
            // Insert initialization code here
        }

        return self;
    }
}

@end

Ich wollte nur diese verlassen hier, damit ich es nicht verlieren. Der Vorteil dieser ist, dass es in Interface, verwendbar ist, was ein großer Vorteil ist. Dies wird von einer anderen Frage genommen, die ich fragte :

static Server *instance;

+ (Server *)instance { return instance; }

+ (id)hiddenAlloc
{
    return [super alloc];
}

+ (id)alloc
{
    return [[self instance] retain];
}


+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        instance = [[Server hiddenAlloc] init];
    }
}

- (id) init
{
    if (instance)
        return self;
    self = [super init];
    if (self != nil) {
        // whatever
    }
    return self;
}
static mySingleton *obj=nil;

@implementation mySingleton

-(id) init {
    if(obj != nil){     
        [self release];
        return obj;
    } else if(self = [super init]) {
        obj = self;
    }   
    return obj;
}

+(mySingleton*) getSharedInstance {
    @synchronized(self){
        if(obj == nil) {
            obj = [[mySingleton alloc] init];
        }
    }
    return obj;
}

- (id)retain {
    return self;
}

- (id)copy {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
    if(obj != self){
        [super release];
    }
    //do nothing
}

- (id)autorelease {
    return self;
}

-(void) dealloc {
    [super dealloc];
}
@end

Ich weiß, dass es eine Menge Kommentare zu dieser „Frage“ ist, aber ich sehe nicht viele Leute mit einem Makro was darauf hindeutet, das Singleton zu definieren. Es ist so ein gemeinsames Muster und ein Makro vereinfacht die Singleton.

Hier sind die Makros ich auf mehreren ObjC Implementierungen auf Basis habe ich gesehen habe.

Singeton.h

/**
 @abstract  Helps define the interface of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the implementation.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonInterface(TYPE, NAME) \
+ (TYPE *)NAME;


/**
 @abstract  Helps define the implementation of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the interface.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+ (void)initialize \
{ \
    static BOOL initialized = NO; \
    if(!initialized) \
    { \
        initialized = YES; \
        __ ## NAME = [[TYPE alloc] init]; \
    } \
} \
\
\
+ (TYPE *)NAME \
{ \
    return __ ## NAME; \
}

Anwendungsbeispiel:

MyManager.h

@interface MyManager

SingletonInterface(MyManager, sharedManager);

// ...

@end

MyManager.m

@implementation MyManager

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}

SingletonImplementation(MyManager, sharedManager);

// ...

@end

Warum ein Interface Makro, wenn es fast leer ist? Code Kohärenz zwischen den Kopf- und Code-Dateien; Wartbarkeit, falls Sie weitere automatische Verfahren oder es hinzufügen drehen.

Ich bin mit der Methode initialize die Singleton zu erstellen, wie hier in der populärste Antwort verwendet wird (zum Zeitpunkt des Schreibens).

Mit Objective C-Klasse Methoden können wir vermeiden, nur das Singletonmuster mit der üblichen Weise aus:

[[Librarian sharedInstance] openLibrary]

zu:

[Librarian openLibrary]

durch die Klasse innerhalb einer anderen Klasse Verpackung, die gerade hat Klassenmethoden , auf diese Weise gibt es keine Möglichkeit der doppelten Instanzen zufällig zu schaffen, da wir keine Instanz erstellen!

Ich schrieb einen detaillierteren Blog hier :)

Um das Beispiel von @ erweitert robbie-hanson ...

static MySingleton* sharedSingleton = nil;

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

- (id)init {
    self = [super init];
    if (self) {
        // Member initialization here.
    }
    return self;
}

Mein Weg ist einfach so:

static id instanceOfXXX = nil;

+ (id) sharedXXX
{
    static volatile BOOL initialized = NO;

    if (!initialized)
    {
        @synchronized([XXX class])
        {
            if (!initialized)
            {
                instanceOfXXX = [[XXX alloc] init];
                initialized = YES;
            }
        }
    }

    return instanceOfXXX;
}

Wenn die Singleton bereits initialisiert wird, wird der LOCK-Block nicht eingegeben werden. Die zweite Prüfung if (! Initialisiert) ist sicherzustellen, dass es noch nicht initialisiert wird, wenn der aktuelle Thread die Sperre erhält.

Ich habe nicht durch alle Lösungen lesen, so vergeben, wenn dieser Code redundant ist.

Dies ist die Threadsicherheit Umsetzung meiner Meinung nach.

+(SingletonObject *) sharedManager
{
    static SingletonObject * sharedResourcesObj = nil;

    @synchronized(self)
    {
        if (!sharedResourcesObj)
        {
            sharedResourcesObj = [[SingletonObject alloc] init];
        }
    }

    return sharedResourcesObj;
}

Normalerweise verwende ich Code in etwa ähnlich wie bei Ben Hoffstein Antwort (die ich auch aus Wikipedia bekam). Ich benutze es für die von Chris Hanson in seinem Kommentar genannten Gründe.

Aber manchmal habe ich eine Notwendigkeit, einen Singleton in eine NIB zu platzieren, und in diesem Fall verwende ich die folgende:

@implementation Singleton

static Singleton *singleton = nil;

- (id)init {
    static BOOL initialized = NO;
    if (!initialized) {
        self = [super init];
        singleton = self;
        initialized = YES;
    }
    return self;
}

+ (id)allocWithZone:(NSZone*)zone {
    @synchronized (self) {
        if (!singleton)
            singleton = [super allocWithZone:zone];     
    }
    return singleton;
}

+ (Singleton*)sharedSingleton {
    if (!singleton)
        [[Singleton alloc] init];
    return singleton;
}

@end

Ich lasse die Umsetzung von -retain (etc.) an den Leser, obwohl der obige Code ist alles, was Sie in einer Garbage Collection-Umgebung benötigen.

Die akzeptierte Antwort, obwohl es kompiliert, ist falsch.

+ (MySingleton*)sharedInstance
{
    @synchronized(self)  <-------- self does not exist at class scope
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}

Pro Apple-Dokumentation:

... Sie können einen ähnlichen Ansatz nehmen die Klassenmethoden der zugehörigen Klasse, mit dem Klasse-Objekt anstelle selbst.

synchronisieren

Auch wenn mit selbst funktioniert, soll es nicht, und das sieht aus wie ein Kopieren und Einfügen Fehler zu mir. Die korrekte Implementierung für eine Klasse Factory-Methode wäre:

+ (MySingleton*)getInstance
{
    @synchronized([MySingleton class]) 
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top