Ti chiedi se questo è il "modo migliore per creare singleton".
Innanzitutto, sì, questa è una soluzione thread-safe. Questo dispatch_once
modello è il modo moderno e sicuro per generare singleton in Objective-C. Nessuna preoccupazione lì.
Hai chiesto, tuttavia, se questo è il modo "migliore" per farlo. Si dovrebbe riconoscere, tuttavia, che instancetype
e [[self alloc] init]
è potenzialmente fuorviante se utilizzato in combinazione con i singoli.
Il vantaggio instancetype
è che è un modo inequivocabile di dichiarare che la classe può essere sottoclassata senza ricorrere a un tipo di id
, come abbiamo dovuto fare in passato.
Ma static
in questo metodo si presentano sfide di sottoclasse. E se ImageCache
e i BlobCache
singoli fossero entrambe le sottoclassi di una Cache
superclasse senza implementare il proprio sharedCache
metodo?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
Perché questo funzioni, dovresti assicurarti che le sottoclassi implementino il loro sharedInstance
metodo (o come lo chiami per la tua classe particolare).
In conclusione, l'originale sharedInstance
sembra supportare le sottoclassi, ma non lo farà. Se si intende supportare la sottoclasse, almeno includere la documentazione che avverte i futuri sviluppatori che devono ignorare questo metodo.
Per una migliore interoperabilità con Swift, probabilmente si desidera definire questo come una proprietà, non un metodo di classe, ad esempio:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Quindi puoi andare avanti e scrivere un getter per questa proprietà (l'implementazione utilizzerà il dispatch_once
modello che hai suggerito):
+ (Foo *)sharedFoo { ... }
Il vantaggio è che se un utente Swift lo usa, farebbe qualcosa del tipo:
let foo = Foo.shared
Nota, no ()
, perché l'abbiamo implementato come proprietà. A partire da Swift 3, in questo modo si accede generalmente ai singoli. Quindi definirlo come proprietà aiuta a facilitare tale interoperabilità.
A parte questo, se osservi come Apple sta definendo i loro singoli, questo è lo schema che hanno adottato, ad esempio il loro NSURLSession
singleton è definito come segue:
@property (class, readonly, strong) NSURLSession *sharedSession;
Un'altra considerazione di interoperabilità di Swift molto minore era il nome del singleton. È meglio se puoi incorporare il nome del tipo, piuttosto che sharedInstance
. Ad esempio, se la classe fosse Foo
, è possibile definire la proprietà singleton come sharedFoo
. O se la lezione fosse DatabaseManager
, potresti chiamare la proprietà sharedManager
. Quindi gli utenti Swift potrebbero fare:
let foo = Foo.shared
let manager = DatabaseManager.shared
Chiaramente, se vuoi davvero usarlo sharedInstance
, puoi sempre dichiarare il nome Swift se vuoi:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
Chiaramente, quando scriviamo il codice Objective-C, non dovremmo lasciare che l'interoperabilità di Swift superi altre considerazioni di progettazione, ma comunque, se possiamo scrivere un codice che supporta con grazia entrambi i linguaggi, è preferibile.
Concordo con gli altri che sottolineano che se si desidera che questo sia un vero singleton in cui gli sviluppatori non possono / non debbano (involontariamente) istanziare le proprie istanze, il unavailable
qualificatore è attivo init
ed new
è prudente.