334 votes

A quoi doit ressembler mon singleton Objective-C ?

Ma méthode d'accès au singleton est généralement une variante de :

static MyClass *gInstance = NULL;

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

    return(gInstance);
}

Que pourrais-je faire pour améliorer cette situation ?

207voto

Robbie Hanson Points 3090

Une autre option consiste à utiliser le +(void)initialize méthode. Extrait de la documentation :

Le runtime envoie initialize à chaque classe d'un programme exactement une fois, juste avant que la classe, ou toute classe qui en hérite, n'envoie son premier message depuis le programme. (Ainsi, la méthode peut ne jamais être invoquée si la classe n'est pas utilisée.) Le runtime envoie la méthode initialize aux classes de manière sécurisée. Les superclasses reçoivent ce message avant leurs sous-classes.

Vous pourriez donc faire quelque chose comme ça :

static MySingleton *sharedSingleton;

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

95voto

Ben Hoffstein Points 44398
@interface MySingleton : NSObject
{
}

+ (MySingleton *)sharedSingleton;
@end

@implementation MySingleton

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

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

    return sharedSingleton;
  }
}

@end

[Source]

59voto

Colin Barrett Points 3581

Selon mon autre réponse ci-dessous, je pense que vous devriez le faire :

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

58voto

Louis Gerbarg Points 33025

Depuis Kendall a posté un singleton threadsafe qui tente d'éviter les coûts de verrouillage, j'ai pensé que je pourrais en lancer un aussi :

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

Ok, laissez-moi vous expliquer comment ça marche :

  1. Une affaire rapide : En exécution normale sharedInstance a déjà été défini, de sorte que le while n'est jamais exécutée et la fonction revient après avoir simplement vérifié l'existence de la variable ;

  2. Une affaire lente : Si sharedInstance n'existe pas, une instance est allouée et copiée à l'aide d'un Compare And Swap ('CAS') ;

  3. Affaire contestée : Si deux threads tentent tous deux d'appeler sharedInstance en même temps ET sharedInstance n'existe pas au même moment, ils initialiseront tous deux de nouvelles instances du singleton et tenteront de le CASer en position. Celui qui gagne le CAS retourne immédiatement, celui qui perd libère l'instance qu'il vient d'allouer et retourne la valeur (maintenant définie) sharedInstance . L'unique OSAtomicCompareAndSwapPtrBarrier agit à la fois comme une barrière d'écriture pour le thread de réglage et comme une barrière de lecture du thread de test.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X