J'ai trouvé des informations sur le Net pour créer une classe Singleton à l'aide de GCD. C'est cool parce que c'est le fil-en sécurité avec des frais généraux très bas. Malheureusement, je n'ai pas pu trouver des solutions complètes mais seulement des extraits de la méthode SharedInstance. J'ai donc fait ma propre classe en utilisant la méthode d'essai et d'erreur - et et voici - 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
S'il vous plaît n'hésitez pas à commenter et dites-moi si je manque quelque chose ou fais quelque chose de complètement faux;)
Bravo Stefan
Rester simple:
+(instancetype)sharedInstance
{
static dispatch_once_t pred;
static id sharedInstance = nil;
dispatch_once(&pred, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (void)dealloc
{
// implement -dealloc & remove abort() when refactoring for
// non-singleton use.
abort();
}
C'est ça. Remplacer retain
, release
, retainCount
_ et le reste consiste à cacher des bogues et à ajouter un tas de lignes de code inutile. Chaque ligne de code est un bug qui attend peut arriver. En réalité, si vous causez dealloc
à appeler sur votre instance partagée, vous avez un bogue très grave dans votre application. Ce bug doit être corrigé, non caché.
Cette approche se prête également à refactoriser pour soutenir les modes d'utilisation non singleton. À peu près, chaque singleton qui survit au-delà de quelques sorties sera éventuellement refouturé dans une forme non singleton. Certains (comme NSFileManager
) continuent de soutenir un mode singleton tout en soutenant instanciation arbitraire.
Notez que ce qui précède "fonctionne simplement" en arc.
// 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 rentrante , donc qui se fait appeler à l'intérieur du bloc de dispatch_once va se bloquer le programme.
Ne tentez pas de manière défensive contre vous-même. Si vous n'êtes pas le codage d'un cadre, traiter votre classe comme d'habitude puis coller l'idiome singleton ci-dessus. Pensez à l'idiome singleton comme méthode pratique, non pas comme un trait déterminant de votre classe. Vous voulez traiter votre classe comme une classe normale lors de tests unitaires, il est donc OK pour laisser un constructeur accessible.
Ne vous embêtez pas avec allocWithZone:
alloc
. Zones de mémoire ne sont plus utilisés dans Objective-C si allocWithZone:
ne sont conservés que pour la compatibilité avec l'ancien code.NSAllocateObject()
et class_createInstance()
.Une méthode de fabrication singleton renvoie toujours l'un de ces trois types:
id
pour indiquer le type de retour est pas complètement connue (cas où vous construisez un cluster de classe).instancetype
pour indiquer que le type de retour est une instance de la classe englobante.MySingleton
dans l'exemple) pour le maintenir simple.Depuis que vous avez taguée cette iOS, une alternative à un singleton sauve le Ivar au délégué de l'application, puis en utilisant une macro de commodité que vous pouvez redéfinir si vous changez d'avis:
#define coreDataManager() \
((AppDelegate*)[[UIApplication sharedApplication] delegate]).coreDataManager
Si vous souhaitez tester votre appareil à tester votre singleton, vous devez également le faire pour que vous puissiez le remplacer par une simulation singleton et/ou le réinitialiser à 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