Quelle est la raison exacte d'utiliser dispatch_once dans l'accesseur d'instance partagée d'un singleton sous ARC?
+ (MyClass *)sharedInstance
{
// Static local predicate must be initialized to 0
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
N'est-ce pas une mauvaise idée d'instancier le singleton de manière asynchrone en arrière-plan? Je veux dire, que se passe-t-il si je demande cette instance partagée et que je m'en sers immédiatement, mais dispatch_once prend jusqu'à Noël pour créer mon objet? Ca ne revient pas tout de suite non? Au moins, cela semble être l’essentiel du Grand Central Dispatch.
Alors pourquoi font-ils cela?
dispatch_once()
est absolument synchrone. Toutes les méthodes GCD ne font pas les choses de manière asynchrone (le cas d'espèce, dispatch_sync()
est synchrone). L'utilisation de dispatch_once()
remplace l'idiome suivant:
+ (MyClass *)sharedInstance {
static MyClass *sharedInstance;
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[MyClass alloc] init];
}
}
return sharedInstance;
}
L'avantage de dispatch_once()
par rapport à cela est que c'est plus rapide. Il est également sémantiquement plus net, car il vous protège également de plusieurs threads qui effectuent alloc init de votre instance partagée - s’ils essaient tous au même moment. Cela ne permettra pas la création de deux instances. Toute l'idée de dispatch_once()
est "effectuer quelque chose une fois et une fois", et c'est précisément ce que nous faisons.
Parce qu'il ne fonctionnera qu'une fois. Donc, si vous essayez d'y accéder deux fois à partir de threads différents, cela ne posera pas de problème.
Mike Ash a une description complète dans son billet de blog Entretien et alimentation des singletons .
Tous les blocs GCD ne sont pas exécutés de manière asynchrone.