29 votes

Corriger le Pattern Singleton Objective-C (iOS)?

J'ai trouvé quelques informations sur le net pour créer une classe singleton à l'aide de PGCD. C'est cool parce que c'est thread-safe avec de très faible hauteur. Malheureusement je ne pouvais pas trouver de solutions complètes, mais seulement des extraits de l'sharedInstance méthode. J'ai donc fait ma propre classe à l'aide de la méthode essai-erreur - et et voila - ce qui suit est sorti:

@implementation MySingleton

// MARK: -
// MARK: Singleton Pattern using GCD

+ (id)allocWithZone:(NSZone *)zone { return [[self sharedInstance] retain]; }
- (id)copyWithZone:(NSZone *)zone { return self; }
- (id)autorelease { return self; }
- (oneway void)release { /* Singletons can't be released */ }
- (void)dealloc { [super dealloc]; /* should never be called */ }
- (id)retain { return self; }
- (NSUInteger)retainCount { return NSUIntegerMax; /* That's soooo non-zero */ }

+ (MySingleton *)sharedInstance
{
    static MySingleton * instance = nil;

    static dispatch_once_t predicate;   
    dispatch_once(&predicate, ^{
        // --- call to super avoids a deadlock with the above allocWithZone
        instance = [[super allocWithZone:nil] init];
    });

    return instance;
}

// MARK: -
// MARK: Initialization

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

@end

N'hésitez pas à commenter et me dire si j'ai raté quelque chose ou de faire quelque chose de complètement faux ;)

Cheers Stefan

82voto

bbum Points 124887

Keep it simple:

+(instancetype)sharedInstance
{
    static dispatch_once_t pred;
    static id sharedInstance = nil;
    dispatch_once(&pred, ^{
        sharedInstance = [[[self class] alloc] init];
    });
    return sharedInstance;
}

- (void)dealloc
{
    // implement -dealloc & remove abort() when refactoring for
    // non-singleton use.
    abort();
}

Qui est-il. Primordial retain, release, retainCount et le reste est juste la clandestinité de bugs et l'ajout d'un tas de lignes de code inutile. Chaque ligne de code est un bug en attente de se produire. En réalité, si vous en êtes la cause dealloc d'être appelé sur votre instance partagée, vous avez un très sérieux problème dans votre application. Ce bug devrait être corrigé, et non pas caché.

Cette approche se prête également à la refactorisation à l'appui de la non-singleton utilisation de modes. À peu près chaque singleton qui survit au-delà de quelques releases sera finalement refait dans une situation de non-singleton forme. Certains (comme NSFileManager) continuer à soutenir un singleton mode tout en soutenant l'arbitraire de l'instanciation.

Notez que le ci-dessus a aussi "fonctionne" dans l'ARC.

19voto

Jano Points 37593
// See Mike Ash "Care and Feeding of Singletons"
// See Cocoa Samurai "Singletons: You're doing them wrong"
+(MySingleton *)singleton {
    static dispatch_once_t pred;
    static MySingleton *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[MySingleton alloc] init];
        shared.someIvar = @"blah";
    });
    return shared;
}

Sachez que dispatch_once n'est pas réentrant, afin de l'appelant lui-même de l'intérieur de la dispatch_once bloc de blocage du programme.

N'essayez pas de code défensivement contre vous-même. Si vous n'êtes pas le codage d'un cadre, de traiter votre classe normale puis coller le singleton idiome ci-dessus. Pensez à le singleton idiome comme une méthode de convenance, non pas comme une caractéristique de votre classe. Vous voulez traiter votre classe comme une classe normale au cours de tests unitaires, donc c'est OK pour laisser accessible constructeur.

Ne vous embêtez pas à l'aide de allocWithZone:

  • Il ignore son argument et se comporte exactement comme alloc. Les zones mémoire sont plus utilisés en Objective-C, afin de allocWithZone: ne sont conservés que pour la compatibilité avec l'ancien code.
  • Il ne fonctionne pas. Vous ne pouvez pas appliquer singleton comportement en Objective-C, parce que plus de cas peut toujours être créé à l'aide d' NSAllocateObject() et class_createInstance().

Un singleton, factory méthode renvoie toujours l'un de ces trois types:

  • id pour indiquer le type de retour n'est pas entièrement connue (cas où vous êtes la construction d'une classe de cluster).
  • instancetype pour indiquer que le type retourné est une instance de la classe englobante.
  • Le nom de la classe elle-même (MySingleton dans l'exemple) pour garder les choses simples.

Depuis que vous avez marqués ce iOS, une alternative à un singleton est une économie de la ivar pour le délégué d'application, puis à l'aide d'une commodité de macro que vous pouvez redéfinir si vous changez d'avis:

#define coreDataManager() \
        ((AppDelegate*)[[UIApplication sharedApplication] delegate]).coreDataManager

1voto

ToddH Points 1134

Si vous voulez de l'unité de test de votre singleton, vous devez également faire en sorte que vous pouvez le remplacer par un simulacre de singleton et/ou de le remettre à la normale:

@implementation ArticleManager

static ArticleManager *_sharedInstance = nil;
static dispatch_once_t once_token = 0;

+(ArticleManager *)sharedInstance {
    dispatch_once(&once_token, ^{
        if (_sharedInstance == nil) {
            _sharedInstance = [[ArticleManager alloc] init];
        }
    });
    return _sharedInstance;
}

+(void)setSharedInstance:(ArticleManager *)instance {
    once_token = 0; // resets the once_token so dispatch_once will run again
    _sharedInstance = instance;
}

@end

0voto

Simon Points 5686

Il y a un excellent article de Matt Gallagher sur les singletons ici. Aussi, il comprend un cool macro pour la réalisation de singleton synthèse ici (dans l'article). J'utilise beaucoup dans mes programmes.

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