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?

0voto

Nate Punkte 12396

Ich weiß, dass es eine Menge Kommentare zu dieser "Frage" gibt, aber ich sehe nicht viele Leute, die vorschlagen, ein Makro zu verwenden, um das Singleton zu definieren. Es ist ein so gängiges Muster und ein Makro vereinfacht das Singleton erheblich.

Hier sind die Makros, die ich auf der Grundlage verschiedener Objc-Implementierungen, die ich gesehen habe, geschrieben 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; \
}

Beispiel für die Verwendung:

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 Schnittstellenmakro, wenn es fast leer ist? Code-Konsistenz zwischen den Header- und den Code-Dateien; Wartbarkeit für den Fall, dass Sie weitere automatische Methoden hinzufügen oder sie ändern möchten.

Ich verwende die Initialisierungsmethode, um das Singleton zu erstellen, wie in der beliebtesten Antwort hier (zum Zeitpunkt des Schreibens) verwendet wird.

0voto

Dan Rosenstark Punkte 66285

Ich wollte das nur hier lassen, damit ich es nicht verliere. Der Vorteil dieser ist, dass es in InterfaceBuilder verwendbar ist, was ein RIESIGER Vorteil ist. Dies ist ein Auszug aus einer anderen Frage, die ich gestellt habe :

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

0voto

user370199 Punkte 1
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

0voto

Rob Dotson Punkte 1

Sie wollen nicht auf self synchronisieren... Da das self-Objekt noch nicht existiert! Du endest damit, dass du auf einen temporären id-Wert sperrst. Sie wollen sicherstellen, dass niemand sonst die Methoden der Klasse ausführen kann (sharedInstance, alloc, allocWithZone:, etc.), also müssen Sie stattdessen auf das Klassenobjekt synchronisieren:

@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

-4voto

Gregory Higley Punkte 15136

Normalerweise verwende ich einen Code, der in etwa dem in Ben Hoffsteins Antwort entspricht (die ich auch aus Wikipedia entnommen habe). Ich verwende ihn aus den von Chris Hanson in seinem Kommentar genannten Gründen.

Manchmal muss ich jedoch ein Singleton in eine NIB einfügen, und in diesem Fall verwende ich Folgendes:

@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 überlasse die Umsetzung der -retain (usw.) an den Leser weitergeben, obwohl der obige Code in einer Garbage-Collector-Umgebung ausreicht.

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