Lo que tiene está bien, aunque podría mover la declaración de variable global a su método de instancia + (el único lugar donde debe usarse, a menos que también permita que se establezca) y usar un nombre como + defaultMyClass o + sharedMyClass para su método. + instancia no revela la intención.
Chris Hanson
Dado que es poco probable que la 'respuesta' a esta pregunta cambie pronto, estoy colocando un bloqueo histórico en la pregunta. Dos razones 1) Muchos puntos de vista, votos y buen contenido 2) Para evitar el yoyo de abierto / cerrado. Fue una gran pregunta para su tiempo, pero las preguntas de este tipo no son apropiadas para Stack Overflow. Ahora tenemos Revisión de código para verificar el código de trabajo. Lleve toda la discusión de esta pregunta a esta meta pregunta .
George Stocker
Respuestas:
207
Otra opción es usar el +(void)initializemétodo. De la documentación:
El tiempo de ejecución se envía initializea cada clase en un programa exactamente una vez justo antes de que la clase, o cualquier clase que herede de ella, envíe su primer mensaje desde el programa. (Por lo tanto, el método nunca se puede invocar si no se usa la clase). El tiempo de ejecución envía el initializemensaje a las clases de manera segura para subprocesos. Las superclases reciben este mensaje antes que sus subclases.
Si el tiempo de ejecución solo llamará a esto una vez, ¿qué hace el BOOL? ¿Es eso una precaución en caso de que alguien llame a esta función explícitamente desde su código?
Aftermathew
55
Sí, es una precaución ya que la función también se puede llamar directamente.
Robbie Hanson
33
Esto también es necesario porque podría haber subclases. Si no anulan, +initializesu implementación de superclases se llamará si la subclase se usa por primera vez.
Sven
3
@Paul puedes anular el releasemétodo y dejarlo vacío. :)
44
@aryaxt: de los documentos enumerados, esto ya es seguro para subprocesos. Entonces, la llamada es una vez por tiempo de ejecución - período. Esta parece ser la solución correcta, segura para subprocesos y óptimamente eficiente.
Esto es todo lo que normalmente debes usar para los singletons. Entre otras cosas, mantener tus clases por separado instanciables hace que sean más fáciles de probar, porque puedes probar instancias separadas en lugar de tener una forma de restablecer su estado.
Chris Hanson
3
Stig Brautaset: No, no está bien omitir el @synchronized en este ejemplo. Está allí para manejar la posible condición de carrera de dos subprocesos que ejecutan esta función estática al mismo tiempo, ambos superando la prueba "if (! SharedSingleton)" al mismo tiempo, y dando como resultado dos [MySingleton alloc] s. .. El {synchronized {scope block} obliga a ese hipotético segundo thread a esperar que el primer thread salga del {scope block} antes de que se le permita continuar. ¡Espero que esto ayude! =)
MechEthan
3
¿Qué impide que alguien siga haciendo su propia instancia del objeto? MySingleton *s = [[MySingelton alloc] init];
lindon fox
1
@lindonfox ¿Cuál es la respuesta a tu pregunta?
Raffi Khatchadourian
1
@ Raffi: lo siento, creo que debí haber olvidado pegar mi respuesta. De todos modos, obtuve el libro Pro Objective-C Design Patterns for iOSy explica cómo se hace un singelton "estricto". Básicamente, dado que no puede hacer que los métodos de inicio sean privados, debe anular los métodos alloc y copy. Entonces, si intenta hacer algo como [[MySingelton alloc] init]esto, obtendrá un error de tiempo de ejecución (aunque desafortunadamente no es un error de tiempo de compilación). No entiendo cómo todos los detalles de la creación de objetos, pero se implementa + (id) allocWithZone:(NSZone *)zonelo que se llama ensharedSingleton
Lindon zorro
59
Según mi otra respuesta a continuación, creo que deberías estar haciendo:
No te molestes con todo lo que estás haciendo arriba. Haga que sus singletons (con suerte extremadamente pocos) sean instanciables por separado, y solo tenga un método compartido / predeterminado. Lo que has hecho solo es necesario si realmente solo quieres una sola instancia de tu clase. Lo que no sabes, especialmente. para pruebas unitarias.
Chris Hanson
La cuestión es que este es el código de muestra de Apple para "crear un singleton". Pero sí, tienes toda la razón.
Colin Barrett
1
El código de muestra de Apple es correcto si desea un singleton "verdadero" (es decir, un objeto que solo se puede instanciar una vez), pero como dice Chris, esto rara vez es lo que quiere o necesita, mientras que algún tipo de instancia compartida configurable es lo que usted desea. generalmente quiero.
Luke Redpath el
Aquí hay una macro para el método anterior: gist.github.com/1057420 . Esto es lo que yo uso.
Kobski
1
Dejando a un lado las pruebas unitarias, no hay nada en contra de esta solución, ¿correcto? Y es rápido y seguro.
LearnCocos2D
58
Dado que Kendall publicó un singleton seguro para hilos que intenta evitar los costos de bloqueo, pensé en tirar uno también:
Caso rápido: en la ejecución normal sharedInstanceya se ha establecido, por lo que el whilebucle nunca se ejecuta y la función regresa después de simplemente probar la existencia de la variable;
Caso lento: si sharedInstanceno existe, se asigna una instancia y se copia en ella utilizando un Comparar y cambiar ('CAS');
Caso contenido: si dos subprocesos intentan llamar sharedInstanceal mismo tiempo YsharedInstance no existe al mismo tiempo, ambos inicializarán nuevas instancias del singleton e intentarán ponerlo en CAS. El que gane el CAS regresa inmediatamente, el que pierde libera la instancia que acaba de asignar y devuelve el (ahora configurado) sharedInstance. El single OSAtomicCompareAndSwapPtrBarrieractúa como una barrera de escritura para el hilo de configuración y una barrera de lectura del hilo de prueba.
Esto es una exageración completa por lo menos una vez que puede suceder durante la vida útil de una aplicación. Sin embargo, es correcto y la técnica de comparar e intercambiar es una herramienta útil para conocer, entonces +1.
Steve Madsen
Buena respuesta: la familia OSAtomic es algo bueno que se debe saber
Bill
1
@Louis: ¡Una respuesta increíble, realmente esclarecedora! Sin embargo, una pregunta: ¿qué debería hacer mi initmétodo en su enfoque? Lanzar una excepción cuando sharedInstancese inicializa no es una buena idea, creo. ¿Qué hacer para evitar que el usuario llame initdirectamente muchas veces?
matm
2
Generalmente no lo evito. A menudo hay razones válidas para permitir que lo que generalmente es un singleton se multiplique, el más común es para ciertos tipos de pruebas unitarias. Si realmente quisiera imponer una instancia única, probablemente haría que el método init verificara si el global existía, y si lo tuviera, lo liberaría y devolvería el global.
Noté que clang se queja de una fuga si no asigna el resultado de [[self alloc] init]sharedInst.
pix0r
Subvertir init como este es un enfoque bastante feo de la OMI. No te metas con init y / o la creación real del objeto. Si, en cambio, busca un punto de acceso controlado a una instancia compartida, mientras que no está insertando el singleton en el objeto, tendrá un momento más feliz si escribe pruebas, etc. Los singletons duros son demasiado utilizados.
La documentación de Apple recomienda que verifique el tipo de clase en su bloque de inicialización. Porque las subclases llaman a initialize por defecto. Existe un caso no obvio en el que se pueden crear subclases indirectamente a través de KVO. Porque si agrega la siguiente línea en otra clase:
Tengo una variación interesante en sharedInstance que es segura para subprocesos, pero no se bloquea después de la inicialización. Todavía no estoy lo suficientemente seguro como para modificar la respuesta principal como se solicitó, pero la presento para una discusión adicional:
// Volatile to make sure we are not foiled by CPU cachesstaticvolatileALBackendRequestManager*sharedInstance;// There's no need to call this directly, as method swizzling in sharedInstance// means this will get called after the singleton is initialized.+(MySingleton*)simpleSharedInstance
{return(MySingleton*)sharedInstance;}+(MySingleton*)sharedInstance
{@synchronized(self){if(sharedInstance == nil){
sharedInstance =[[MySingleton alloc] init];// Replace expensive thread-safe method // with the simpler one that just returns the allocated instance.
SEL origSel =@selector(sharedInstance);
SEL newSel =@selector(simpleSharedInstance);Method origMethod = class_getClassMethod(self, origSel);Method newMethod = class_getClassMethod(self, newSel);
method_exchangeImplementations(origMethod, newMethod);}}return(MySingleton*)sharedInstance;}
+1 eso es realmente intrigante. Podría usar class_replaceMethodpara transformarme sharedInstanceen un clon de simpleSharedInstance. De esa forma, nunca más tendrá que preocuparse por adquirir un @synchronizedcandado nuevamente.
Dave DeLong
Es el mismo efecto, usar exchangeImplementations significa que después de init cuando llamas a sharedInstance, realmente estás llamando a simpleSharedInstance. De hecho, comencé con replaceMethod, pero decidí que era mejor cambiar las implementaciones para que el original aún existiera si fuera necesario ...
Kendall Helmstetter Gelner
En otras pruebas, no pude hacer que replaceMethod funcione: en llamadas repetidas, el código todavía llamaba original SharedInstance en lugar de simpleSharedInstance. Creo que puede ser porque ambos son métodos de nivel de clase ... El reemplazo que usé fue: class_replaceMethod (self, origSel, method_getImplementation (newMethod), method_getTypeEncoding (newMethod)); y algunas variaciones de los mismos. Puedo verificar el código que he publicado y se llama a simpleSharedInstance después del primer paso a través de sharedInstance.
Kendall Helmstetter Gelner
Puede hacer una versión segura para subprocesos que no pague los costos de bloqueo después de la inicialización sin hacer un montón de mucking de tiempo de ejecución, he publicado una implementación a continuación.
Louis Gerbarg
1
+1 gran idea. Me encantan las cosas que uno puede hacer con el tiempo de ejecución. Pero en la mayoría de los casos esto probablemente sea una optimización prematura. Si realmente tuviera que deshacerme del costo de sincronización, probablemente usaría la versión sin bloqueo de Louis.
Asegúrese de leer el encabezado dispatch / once.h para comprender lo que está sucediendo. En este caso, los comentarios del encabezado son más aplicables que los documentos o la página de manual.
La única limitación sobre la clase Singleton es que es una subclase de NSObject. Pero la mayoría de las veces uso singletons en mi código, de hecho, son subclases NSObject, por lo que esta clase realmente me facilita la vida y hace que el código sea más limpio.
Estoy usando su NSSingleton para mi proyecto, y parece ser incompatible con KVO. El asunto es que KVO crea una subclase para cada objeto KVO con el prefijo NSKVONotifying_ MyClass . Y hace que MyClass + se inicialice y se inicien los métodos dos veces.
Oleg Trakhman
Probé esto en el último Xcode y no tuve ningún problema para registrarme o recibir eventos de KVO. Puede verificar esto con el siguiente código: gist.github.com/3065038 Como mencioné en Twitter, los métodos + initialize se llaman una vez para NSSingleton y una vez para cada subclase. Esta es una propiedad de Objective-C.
kevinlawler
Si agrega NSLog(@"initialize: %@", NSStringFromClass([self class]));al +initializemétodo, puede verificar que las clases se inicialicen solo una vez.
No desea sincronizar con uno mismo ... ¡Ya que el objeto propio todavía no existe! Terminas bloqueando un valor de identificación temporal. Desea asegurarse de que nadie más pueda ejecutar métodos de clase (sharedInstance, alloc, allocWithZone :, etc.), por lo que debe sincronizar el objeto de clase en su lugar:
@implementationMYSingletonstaticMYSingleton* sharedInstance = nil;+( id )sharedInstance {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[[MYSingleton alloc ] init ];}return sharedInstance;}+( id )allocWithZone:(NSZone*)zone {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[ super allocWithZone:zone ];}return sharedInstance;}-( id )init {@synchronized([MYSingletonclass]){
self =[ super init ];if( self != nil ){// Insert initialization code here}return self;}}@end
El resto de los métodos, métodos de acceso, métodos de mutadores, etc. deben sincronizarse en uno mismo. Todos los métodos e inicializadores de clase (+) (y probablemente -dealloc) deberían sincronizarse en el objeto de clase. Puede evitar tener que sincronizar manualmente si usa las propiedades de Objective-C 2.0 en lugar de los métodos de acceso / mutación. Todos object.property y object.property = foo, se sincronizan automáticamente con self.
Rob Dotson el
3
Explica por qué crees que el selfobjeto no existe en un método de clase. El tiempo de ejecución determina qué implementación de método invocar en función del mismo valor exacto que proporciona selfpara cada método (clase o instancia).
dreamlax
2
Dentro de un método de clase, selfestá el objeto de clase. Inténtelo usted mismo:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
CSJs
0
Solo quería dejar esto aquí para no perderlo. La ventaja de este es que se puede usar en InterfaceBuilder, que es una GRAN ventaja. Esto se toma de otra pregunta que hice :
Sé que hay muchos comentarios sobre esta "pregunta", pero no veo que muchas personas sugieran usar una macro para definir el singleton. Es un patrón tan común y una macro simplifica enormemente el singleton.
Aquí están las macros que escribí basadas en varias implementaciones de Objc que he visto.
Singeton.h
/**
@abstract Helps define the interface of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the implementation.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonInterface(TYPE, NAME) \
+(TYPE *)NAME;/**
@abstract Helps define the implementation of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the interface.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+(void)initialize \
{ \
static BOOL initialized = NO; \
if(!initialized) \
{ \
initialized = YES; \
__ ## NAME = [[TYPE alloc] init]; \} \
} \
\
\
+(TYPE *)NAME \
{ \
return __ ## NAME; \}
¿Por qué una macro de interfaz cuando está casi vacía? Consistencia del código entre el encabezado y los archivos de código; mantenibilidad en caso de que desee agregar más métodos automáticos o cambiarlo.
Estoy usando el método initialize para crear el singleton como se usa en la respuesta más popular aquí (al momento de escribir).
Con los métodos de clase Objective C, podemos evitar usar el patrón singleton de la manera habitual, desde:
[[Librarian sharedInstance] openLibrary]
a:
[Librarian openLibrary]
envolviendo la clase dentro de otra clase que solo tiene métodos de clase , de esa manera no hay posibilidad de crear instancias duplicadas accidentalmente, ¡ya que no estamos creando ninguna instancia!
Si el singleton ya está inicializado, no se ingresará el bloque LOCK. La segunda comprobación si (! Initialized) es para asegurarse de que aún no se haya inicializado cuando el hilo actual adquiera el LOCK.
Usualmente uso un código más o menos similar al de la respuesta de Ben Hoffstein (que también saqué de Wikipedia). Lo uso por los motivos expuestos por Chris Hanson en su comentario.
Sin embargo, a veces tengo la necesidad de colocar un singleton en una NIB, y en ese caso uso lo siguiente:
Su código no es seguro para subprocesos. Utiliza sincronizado en el método alloc, pero no en el método init. Verificar el bool inicializado no es seguro para subprocesos.
Mecki
-5
La respuesta aceptada, aunque se compila, es incorrecta.
+(MySingleton*)sharedInstance
{@synchronized(self)<-------- self does not exist at class scope
{if(sharedInstance == nil)
sharedInstance =[[MySingleton alloc] init];}return sharedInstance;}
Por documentación de Apple:
... Puede adoptar un enfoque similar para sincronizar los métodos de clase de la clase asociada, utilizando el objeto Class en lugar de self.
Incluso si usa trabajos por cuenta propia, no debería y esto me parece un error de copiar y pegar. La implementación correcta para un método de clase de fábrica sería:
auto sin duda lo hace existir es ámbito de clase. Se refiere a la clase en lugar de la instancia de la clase. Las clases son (principalmente) objetos de primera clase.
schwa
¿Por qué pones @synchroninzed DENTRO de un método?
user4951
1
Como ya dijo schwa, selfes el objeto de clase dentro de un método de clase. Vea mi comentario para un fragmento que demuestre esto.
jscs
selfexiste, pero usarlo como el identificador pasado @synchronizedsincronizará el acceso a los métodos de la instancia. Como señala @ user490696, hay casos (como singletons) en los que es preferible usar el objeto de clase. De la Guía de programación Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.
Respuestas:
Otra opción es usar el
+(void)initialize
método. De la documentación:Entonces podrías hacer algo similar a esto:
fuente
+initialize
su implementación de superclases se llamará si la subclase se usa por primera vez.release
método y dejarlo vacío. :)[Fuente]
fuente
MySingleton *s = [[MySingelton alloc] init];
Pro Objective-C Design Patterns for iOS
y explica cómo se hace un singelton "estricto". Básicamente, dado que no puede hacer que los métodos de inicio sean privados, debe anular los métodos alloc y copy. Entonces, si intenta hacer algo como[[MySingelton alloc] init]
esto, obtendrá un error de tiempo de ejecución (aunque desafortunadamente no es un error de tiempo de compilación). No entiendo cómo todos los detalles de la creación de objetos, pero se implementa+ (id) allocWithZone:(NSZone *)zone
lo que se llama ensharedSingleton
Según mi otra respuesta a continuación, creo que deberías estar haciendo:
fuente
Dado que Kendall publicó un singleton seguro para hilos que intenta evitar los costos de bloqueo, pensé en tirar uno también:
Bien, déjame explicarte cómo funciona esto:
Caso rápido: en la ejecución normal
sharedInstance
ya se ha establecido, por lo que elwhile
bucle nunca se ejecuta y la función regresa después de simplemente probar la existencia de la variable;Caso lento: si
sharedInstance
no existe, se asigna una instancia y se copia en ella utilizando un Comparar y cambiar ('CAS');Caso contenido: si dos subprocesos intentan llamar
sharedInstance
al mismo tiempo YsharedInstance
no existe al mismo tiempo, ambos inicializarán nuevas instancias del singleton e intentarán ponerlo en CAS. El que gane el CAS regresa inmediatamente, el que pierde libera la instancia que acaba de asignar y devuelve el (ahora configurado)sharedInstance
. El singleOSAtomicCompareAndSwapPtrBarrier
actúa como una barrera de escritura para el hilo de configuración y una barrera de lectura del hilo de prueba.fuente
init
método en su enfoque? Lanzar una excepción cuandosharedInstance
se inicializa no es una buena idea, creo. ¿Qué hacer para evitar que el usuario llameinit
directamente muchas veces?fuente
[[self alloc] init]
sharedInst.Editar: Esta implementación está obsoleta con ARC. Por favor, eche un vistazo a ¿Cómo implemento un singleton Objective-C que sea compatible con ARC? Para una correcta implementación.
Todas las implementaciones de initialize que he leído en otras respuestas comparten un error común.
La documentación de Apple recomienda que verifique el tipo de clase en su bloque de inicialización. Porque las subclases llaman a initialize por defecto. Existe un caso no obvio en el que se pueden crear subclases indirectamente a través de KVO. Porque si agrega la siguiente línea en otra clase:
Objective-C creará implícitamente una subclase de MySingletonClass dando como resultado una segunda activación de
+initialize
.Puede pensar que debe verificar implícitamente la inicialización duplicada en su bloque de inicio como tal:
Pero te dispararás en el pie; o peor, dar a otro desarrollador la oportunidad de pegarse un tiro en el pie.
TL; DR, aquí está mi implementación
(Reemplace ZAssert con nuestra propia macro de aserción; o simplemente NSAssert).
fuente
Una explicación detallada del código de macro Singleton está en el blog Cocoa With Love
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html .
fuente
Tengo una variación interesante en sharedInstance que es segura para subprocesos, pero no se bloquea después de la inicialización. Todavía no estoy lo suficientemente seguro como para modificar la respuesta principal como se solicitó, pero la presento para una discusión adicional:
fuente
class_replaceMethod
para transformarmesharedInstance
en un clon desimpleSharedInstance
. De esa forma, nunca más tendrá que preocuparse por adquirir un@synchronized
candado nuevamente.Respuesta corta: fabulosa.
Respuesta larga: algo así como ...
Asegúrese de leer el encabezado dispatch / once.h para comprender lo que está sucediendo. En este caso, los comentarios del encabezado son más aplicables que los documentos o la página de manual.
fuente
He incluido singleton en una clase, por lo que otras clases pueden heredar propiedades singleton.
Singleton.h:
Singleton.m:
Y aquí hay un ejemplo de alguna clase, que quieres convertirte en soltero.
La única limitación sobre la clase Singleton es que es una subclase de NSObject. Pero la mayoría de las veces uso singletons en mi código, de hecho, son subclases NSObject, por lo que esta clase realmente me facilita la vida y hace que el código sea más limpio.
fuente
@synchronized
es terriblemente lento y debe evitarse.Esto funciona en un entorno no recolectado de basura también.
fuente
¿No debería ser seguro y evitar el costoso bloqueo después de la primera llamada?
fuente
Aquí hay una macro que armé:
http://github.com/cjhanson/Objective-C-Optimized-Singleton
Se basa en el trabajo aquí realizado por Matt Gallagher, pero cambiando la implementación para utilizar el método swizzling como se describe aquí por Dave MacLachlan de Google .
Agradezco comentarios / contribuciones.
fuente
Qué tal si
¿Entonces evitas el costo de sincronización después de la inicialización?
fuente
Para una discusión en profundidad del patrón singleton en Objective-C, mira aquí:
Usando el patrón Singleton en Objective-C
fuente
KLSingleton
fuente
NSLog(@"initialize: %@", NSStringFromClass([self class]));
al+initialize
método, puede verificar que las clases se inicialicen solo una vez.No desea sincronizar con uno mismo ... ¡Ya que el objeto propio todavía no existe! Terminas bloqueando un valor de identificación temporal. Desea asegurarse de que nadie más pueda ejecutar métodos de clase (sharedInstance, alloc, allocWithZone :, etc.), por lo que debe sincronizar el objeto de clase en su lugar:
fuente
self
objeto no existe en un método de clase. El tiempo de ejecución determina qué implementación de método invocar en función del mismo valor exacto que proporcionaself
para cada método (clase o instancia).self
está el objeto de clase. Inténtelo usted mismo:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
Solo quería dejar esto aquí para no perderlo. La ventaja de este es que se puede usar en InterfaceBuilder, que es una GRAN ventaja. Esto se toma de otra pregunta que hice :
fuente
fuente
Sé que hay muchos comentarios sobre esta "pregunta", pero no veo que muchas personas sugieran usar una macro para definir el singleton. Es un patrón tan común y una macro simplifica enormemente el singleton.
Aquí están las macros que escribí basadas en varias implementaciones de Objc que he visto.
Singeton.h
Ejemplo de uso:
MyManager.h
MyManager.m
¿Por qué una macro de interfaz cuando está casi vacía? Consistencia del código entre el encabezado y los archivos de código; mantenibilidad en caso de que desee agregar más métodos automáticos o cambiarlo.
Estoy usando el método initialize para crear el singleton como se usa en la respuesta más popular aquí (al momento de escribir).
fuente
Con los métodos de clase Objective C, podemos evitar usar el patrón singleton de la manera habitual, desde:
a:
envolviendo la clase dentro de otra clase que solo tiene métodos de clase , de esa manera no hay posibilidad de crear instancias duplicadas accidentalmente, ¡ya que no estamos creando ninguna instancia!
Escribí un blog más detallado aquí :)
fuente
Para ampliar el ejemplo de @ robbie-hanson ...
fuente
Mi camino es simple como este:
Si el singleton ya está inicializado, no se ingresará el bloque LOCK. La segunda comprobación si (! Initialized) es para asegurarse de que aún no se haya inicializado cuando el hilo actual adquiera el LOCK.
fuente
initialized
comovolatile
es suficiente. Ver aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf .No he leído todas las soluciones, así que perdona si este código es redundante.
Esta es la implementación más segura para subprocesos en mi opinión.
fuente
Usualmente uso un código más o menos similar al de la respuesta de Ben Hoffstein (que también saqué de Wikipedia). Lo uso por los motivos expuestos por Chris Hanson en su comentario.
Sin embargo, a veces tengo la necesidad de colocar un singleton en una NIB, y en ese caso uso lo siguiente:
Dejo la implementación de
-retain
(etc.) al lector, aunque el código anterior es todo lo que necesita en un entorno de recolección de basura.fuente
La respuesta aceptada, aunque se compila, es incorrecta.
Por documentación de Apple:
... Puede adoptar un enfoque similar para sincronizar los métodos de clase de la clase asociada, utilizando el objeto Class en lugar de self.
Incluso si usa trabajos por cuenta propia, no debería y esto me parece un error de copiar y pegar. La implementación correcta para un método de clase de fábrica sería:
fuente
self
es el objeto de clase dentro de un método de clase. Vea mi comentario para un fragmento que demuestre esto.self
existe, pero usarlo como el identificador pasado@synchronized
sincronizará el acceso a los métodos de la instancia. Como señala @ user490696, hay casos (como singletons) en los que es preferible usar el objeto de clase. De la Guía de programación Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.