ios objective-c singleton grand-central-dispatch

ios - Crea singleton usando dispatch_once de GCD en Objective C



objective-c grand-central-dispatch (9)

tipo de instancia

instancetype es solo una de las muchas extensiones de lenguaje para Objective-C , y se agregan más con cada nueva versión.

Lo sabes, me encanta.

Y tómelo como un ejemplo de cómo prestar atención a los detalles de bajo nivel puede brindarle información sobre nuevas y poderosas formas de transformar Objective-C.

Consulte aquí: instancetype

+ (instancetype)sharedInstance { static dispatch_once_t once; static id sharedInstance; dispatch_once(&once, ^ { sharedInstance = [self new]; }); return sharedInstance; }

+ (Class*)sharedInstance { static dispatch_once_t once; static Class *sharedInstance; dispatch_once(&once, ^ { sharedInstance = [self new]; }); return sharedInstance; }

Si puedes apuntar a iOS 4.0 o superior

Usando GCD, ¿es la mejor manera de crear singleton en Objective C (seguro para subprocesos)?

+ (instancetype)sharedInstance { static dispatch_once_t once; static id sharedInstance; dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; }


Dave tiene razón, eso está perfectamente bien. Si lo desea, puede consultar la documentación de Apple sobre cómo crear un singleton para obtener consejos sobre cómo implementar algunos de los otros métodos para asegurarse de que solo se pueda crear uno si las clases eligen NO usar el método sharedFoo.


Esta es una forma perfectamente aceptable y segura para crear una instancia de su clase. Técnicamente, puede que no sea un "singleton" (en el sentido de que solo puede haber uno de estos objetos), pero siempre y cuando solo uses el método [Foo sharedFoo] para acceder al objeto, esto es suficiente.


MySingleton.h

@interface MySingleton : NSObject +(instancetype)sharedInstance; +(instancetype)alloc __attribute__((unavailable("alloc not available, call sharedInstance instead"))); -(instancetype)init __attribute__((unavailable("init not available, call sharedInstance instead"))); +(instancetype)new __attribute__((unavailable("new not available, call sharedInstance instead"))); -(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead"))); @end

MySingleton.m

@implementation MySingleton +(instancetype)sharedInstance { static dispatch_once_t pred; static id shared = nil; dispatch_once(&pred, ^{ shared = [[super alloc] initUniqueInstance]; }); return shared; } -(instancetype)initUniqueInstance { return [super init]; } @end


Para crear hilo seguro singleton puede hacer esto:

@interface SomeManager : NSObject + (id)sharedManager; @end /* thread safe */ @implementation SomeManager static id sharedManager = nil; + (void)initialize { if (self == [SomeManager class]) { sharedManager = [[self alloc] init]; } } + (id)sharedManager { return sharedManager; } @end

y este blog explica singleton muy bien singletons en objc / cocoa


Preguntas si esta es la "mejor forma de crear singleton".

Unos pocos pensamientos:

  1. Primero, sí, esta es una solución segura para subprocesos. Este patrón dispatch_once es la forma moderna y segura de generar subprocesos en Objective-C. No te preocupes allí.

  2. Sin embargo, usted preguntó si esta es la "mejor" forma de hacerlo. Sin embargo, se debe reconocer que el tipo de instancetype y [[self alloc] init] pueden ser engañosos cuando se usan junto con singletons.

    El beneficio de instancetype es que es una forma inequívoca de declarar que la clase puede ser subclasificada sin tener que recurrir a un tipo de id , como tuvimos que hacer en el pasado.

    Pero la static en este método presenta desafíos de subclasificación. ¿Qué ImageCache si los singletons ImageCache y BlobCache fueran subclases de una superclase Cache sin implementar su propio método sharedCache ?

    ImageCache *imageCache = [ImageCache sharedCache]; // fine BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!

    Para que esto funcione, deberías asegurarte de que las subclases implementen su propio sharedInstance (o como se llame para tu clase en particular).

    En sharedInstance parece que su sharedInstance original de sharedInstance 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.

  3. 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 seguir adelante y escribir un captador para esta propiedad (la implementación usaría el patrón dispatch_once 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. Comenzando Swift 3, así es como generalmente se accede a los singletons. Así que 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 singleton NSURLSession se define de la siguiente manera:

    @property (class, readonly, strong) NSURLSession *sharedSession;

  4. Otra consideración de interoperabilidad Swift muy menor fue el nombre del singleton. Es mejor si puedes incorporar el nombre del tipo, en lugar de la sharedInstance . Por ejemplo, si la clase era Foo , podría definir la propiedad singleton como sharedFoo . O si la clase era DatabaseManager , puede 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 utilizar la sharedInstance , siempre puede declarar el nombre Swift si desea:

    @property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);

    Claramente, al escribir el código de 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 sea compatible con ambos idiomas, es preferible.

  5. Estoy de acuerdo con otros que señalan que si desea que esto sea un verdadero singleton donde los desarrolladores no pueden / no deberían (accidentalmente) crear instancias de sus propias instancias, el calificador unavailable en init y new es prudente.


Puede evitar que la clase se asigne sobrescribiendo el método de asignación.

@implementation MyClass static BOOL useinside = NO; static id _sharedObject = nil; +(id) alloc { if (!useinside) { @throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil]; } else { return [super alloc]; } } +(id)sharedInstance { static dispatch_once_t p = 0; dispatch_once(&p, ^{ useinside = YES; _sharedObject = [[MyClass alloc] init]; useinside = NO; }); // returns the same object each time return _sharedObject; }


Si desea asegurarse de que [[MyClass alloc] init] devuelva el mismo objeto que sharedInstance (no es necesario en mi opinión, pero algunas personas lo desean), eso se puede hacer de manera muy fácil y segura utilizando una segunda dispatch_once:

- (instancetype)init { static dispatch_once_t once; static Class *sharedInstance; dispatch_once(&once, ^ { // Your normal init code goes here. sharedInstance = self; }); return sharedInstance; }

Esto permite que cualquier combinación de [[MyClass alloc] init] y [MyClass sharedInstance] devuelva el mismo objeto; [MyClass sharedInstance] sería un poco más eficiente. Cómo funciona: [MyClass sharedInstance] llamará a [[alloc MyClass] init] una vez. Otro código podría llamarlo también, cualquier número de veces. La primera persona que llama a init realizará la inicialización "normal" y almacenará el objeto singleton en el método init. Cualquier llamada posterior a init ignorará completamente la asignación devuelta y devolverá la misma SharedInstance; El resultado de la asignación será desasignado.

El método + sharedInstance funcionará como siempre lo hizo. Si no es la primera persona que llama a llamar a [[alloc de MyClass] init], entonces el resultado de init no es el resultado de la llamada de asignación, pero está bien.


//Create Singleton +( instancetype )defaultDBManager { static dispatch_once_t onceToken = 0; __strong static id _sharedObject = nil; dispatch_once(&onceToken, ^{ _sharedObject = [[self alloc] init]; }); return _sharedObject; } //In it method -(instancetype)init { self = [super init]; if(self) { //Do your custom initialization } return self; }