Pregunta si esta es la "mejor manera de crear singleton".
Primero, sí, esta es una solución segura para subprocesos. Este dispatch_once
patrón es la forma moderna y segura de subprocesos para generar tonos únicos en Objective-C. No te preocupes allí.
Sin embargo, usted preguntó si esta es la "mejor" forma de hacerlo. Sin embargo, uno debería reconocer que el instancetype
y [[self alloc] init]
es potencialmente engañoso cuando se usa junto con singletons.
El beneficio instancetype
es que es una forma inequívoca de declarar que la clase puede subclasificarse sin recurrir a un tipo de id
, como teníamos que hacer en el pasado.
Pero static
en este método presenta desafíos de subclases. ¿Qué pasaría si ImageCache
y los BlobCache
singletons fueran ambas subclases de una Cache
superclase sin implementar su propio sharedCache
método?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
Para que esto funcione, debe asegurarse de que las subclases implementen su propio sharedInstance
método (o como lo llame para su clase particular).
En pocas palabras, sharedInstance
parece que su original admitirá subclases, pero no lo hará. Si tiene la intención de admitir subclases, al menos incluya documentación que advierta a los futuros desarrolladores que deben anular este método.
Para una mejor interoperabilidad con Swift, probablemente desee definir esto como una propiedad, no un método de clase, por ejemplo:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Luego puede continuar y escribir un captador para esta propiedad (la implementación usaría el dispatch_once
patrón que sugirió):
+ (Foo *)sharedFoo { ... }
El beneficio de esto es que si un usuario de Swift va a usarlo, haría algo como:
let foo = Foo.shared
Tenga en cuenta que no hay ()
, porque lo implementamos como una propiedad. Iniciando Swift 3, así es como generalmente se accede a los singletons. Por lo tanto, definirlo como una propiedad ayuda a facilitar esa interoperabilidad.
Además, si observa cómo Apple está definiendo sus singletons, este es el patrón que han adoptado, por ejemplo, su NSURLSession
singleton se define de la siguiente manera:
@property (class, readonly, strong) NSURLSession *sharedSession;
Otra consideración de interoperabilidad Swift muy menor fue el nombre del singleton. Es mejor si puede incorporar el nombre del tipo, en lugar de sharedInstance
. Por ejemplo, si la clase era Foo
, podría definir la propiedad singleton como sharedFoo
. O si la clase fuera DatabaseManager
, podría llamar a la propiedad sharedManager
. Entonces los usuarios de Swift podrían hacer:
let foo = Foo.shared
let manager = DatabaseManager.shared
Claramente, si realmente desea usar sharedInstance
, siempre puede declarar el nombre de Swift si desea:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
Claramente, al escribir código Objective-C, no debemos permitir que la interoperabilidad de Swift supere otras consideraciones de diseño, pero aún así, si podemos escribir código que admita ambos lenguajes con gracia, es preferible.
Estoy de acuerdo con otros que señalan que si quieres que esto sea un verdadero singleton donde los desarrolladores no pueden / no deben (accidentalmente) crear instancias de sus propias instancias, el unavailable
calificador está activado init
y new
es prudente.