Vous demandez si c'est la "meilleure façon de créer un singleton".
Tout d'abord, oui, c'est une solution thread-safe. Ce dispatch_once
modèle est le moyen moderne et sans fil de générer des singletons dans Objective-C. Pas de soucis là-bas.
Vous avez cependant demandé si c'était la "meilleure" façon de procéder. Il faut cependant reconnaître que le instancetype
et [[self alloc] init]
est potentiellement trompeur lorsqu'il est utilisé conjointement avec des singletons.
L'avantage de instancetype
c'est que c'est une façon non ambiguë de déclarer que la classe peut être sous-classée sans avoir recours à un type de id
, comme nous le faisions autrefois.
Mais le static
dans cette méthode présente des défis de sous-classement. Que faire si ImageCache
et BlobCache
singletons étaient les deux sous - classes d'une Cache
superclasse sans mettre en oeuvre leur propre sharedCache
méthode?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
Pour que cela fonctionne, vous devez vous assurer que les sous-classes implémentent leur propre sharedInstance
méthode (ou tout autre nom pour votre classe particulière).
En bout de ligne, votre original sharedInstance
semble prendre en charge les sous-classes, mais ce ne sera pas le cas. Si vous avez l'intention de prendre en charge le sous-classement, incluez à tout le moins une documentation qui avertit les futurs développeurs qu'ils doivent remplacer cette méthode.
Pour une meilleure interopérabilité avec Swift, vous souhaiterez probablement définir cela comme une propriété, pas une méthode de classe, par exemple:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Ensuite, vous pouvez continuer et écrire un getter pour cette propriété (l'implémentation utiliserait le dispatch_once
modèle que vous avez suggéré):
+ (Foo *)sharedFoo { ... }
L'avantage de ceci est que si un utilisateur Swift va l'utiliser, il ferait quelque chose comme:
let foo = Foo.shared
Remarque, il n'y en a pas ()
, car nous l'avons implémenté en tant que propriété. À partir de Swift 3, c'est ainsi que les singletons sont généralement accessibles. Le définir comme une propriété facilite donc cette interopérabilité.
En passant, si vous regardez comment Apple définit ses singletons, voici le modèle qu'ils ont adopté, par exemple leur NSURLSession
singleton est défini comme suit:
@property (class, readonly, strong) NSURLSession *sharedSession;
Une autre considération d'interopérabilité Swift très mineure était le nom du singleton. Il est préférable d'incorporer le nom du type plutôt que sharedInstance
. Par exemple, si la classe l'était Foo
, vous pourriez définir la propriété singleton comme sharedFoo
. Ou si la classe l'était DatabaseManager
, vous pourriez appeler la propriété sharedManager
. Les utilisateurs de Swift pourraient alors faire:
let foo = Foo.shared
let manager = DatabaseManager.shared
De toute évidence, si vous voulez vraiment l'utiliser sharedInstance
, vous pouvez toujours déclarer le nom Swift si vous le souhaitez:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
De toute évidence, lors de l'écriture de code Objective-C, nous ne devons pas laisser l'interopérabilité Swift l'emporter sur d'autres considérations de conception, mais quand même, si nous pouvons écrire du code qui prend en charge gracieusement les deux langages, c'est préférable.
Je suis d'accord avec d'autres qui soulignent que si vous voulez que ce soit un vrai singleton où les développeurs ne peuvent pas / ne devraient pas (accidentellement) instancier leurs propres instances, le unavailable
qualificatif est activé init
et new
est prudent.