Bueno, una manera fácil de evitar el init
es simplemente no escribir uno para llamarlo a la implementación predeterminada de NSObject (que solo devuelve self
). Luego, para su función sharedInstance
, defina y llame a una función privada que realice un trabajo tipo init cuando crea una instancia de su singleton. (Esto evita que el usuario vuelva a inicializar accidentalmente su singleton).
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ ¡El principal problema es cuando un usuario de tu código llama al alloc
! Por esto, yo personalmente recomiendo ruta de Apple de primordial allocWithZone:
...
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
Esto significa que el usuario seguirá recibiendo la instancia singleton, y pueden utilizar erróneamente como si se asignan, y de forma segura liberarlo una vez desde este la asignación personalizada realiza una retención en el singleton. (Nota: alloc
llama al allocWithZone:
y no necesita ser reemplazado por separado.)
Espero que ayude! Que me haga saber si desea más información ~
EDIT: La expansión de respuesta para proporcionar el ejemplo y más detalles -
Tomando la respuesta de Catfish_Man en consideración, es menudo no es importante para crear un producto único a prueba de balas, y en su lugar, solo escriba algunos comentarios razonables en sus encabezados/documentación y ponga en assert
.
Sin embargo, en mi caso, quería un singleton de carga diferida seguro para subprocesos, es decir, no se asigna hasta que se necesita usar, en lugar de asignarse automáticamente en el inicio de la aplicación. Después de aprender cómo hacerlo de manera segura, pensé que también podría seguir todo el camino con eso.
editar # 2: ahora uso GCD de dispatch_once(...)
de un enfoque seguro para subprocesos de la asignación de un objeto singleton sólo una vez para toda la vida de una aplicación. Ver Apple Docs: GCD dispatch_once. También sigue añadir allocWithZone:
poco anulación del viejo ejemplo Singleton de Apple, y añadí un init privado llamado singletonInit
para evitar que accidentalmente se llama varias veces:
//Hidden/Private initialization
-(void)singletonInit
{
//your init code goes here
}
static HSCloudManager * sharedInstance = nil;
+ (HSCloudManager *) sharedManager {
static dispatch_once_t dispatchOncePredicate = 0;
dispatch_once(&dispatchOncePredicate, ^{
sharedInstance = [[super allocWithZone:NULL] init];
[sharedInstance singletonInit];//Only place you should call singletonInit
});
return sharedInstance;
}
+ (id) allocWithZone:(NSZone *)zone {
//If coder misunderstands this is a singleton, behave properly with
// ref count +1 on alloc anyway, and still return singleton!
return [[HSCloudManager sharedManager] retain];
}
HSCloudManager
subclases NSObject
, y no anula init
dejando sólo el valor por defecto implementación en NSObject
, que según la documentación de Apple solo devuelve auto. Esto significa que [[HSCloudManager alloc] init]
es lo mismo que [[[HSCloud Manager sharedManager] retain] self]
, lo que lo hace seguro tanto para usuarios confundidos como para aplicaciones de subprocesos múltiples como un singleton de carga lenta.
En cuanto a su preocupación sobre la subclasificación de su singleton por parte del usuario, yo diría que simplemente comente/documente claramente. ¡Cualquiera que esté subclasificando ciegamente sin leer en la clase es pidiendo por dolor!
editar # 3: Para compatibilidad ARC, basta con retirar la porción de retener de la anulación allocWithZone:
, pero mantener la anulación.
@yAaak, parece bastante justo , pero me siento como en un ciclo infinito y necesito digerirlo;) ¿Cómo me aseguro de que el proceso de creación de dicho singleton a través de un método privado sea seguro para subprocesos? Otro problema: si sigo la idea de 'allocWithZone' de Apple, se llamará a' init' de NSObject (¿simplemente devuelve 'self' o algo más?) ... y de nuevo el usuario intenta crear una instancia con' alloc' y ' init' ¿cambia algo la inicialización de mis propiedades/ivars y NSObject obtiene 'init' nuevamente? – matm
yAak, tienes razón: enfoque sensato que estás sugiriendo que es suficiente por ahora. Jugaré con singletons un poco más usando tu código :) Creo que tu respuesta es bastante extensa y tiene en cuenta la contra opinión de Catfish_man, así que la acepto. ¡Gracias de nuevo! – matm
Sugiero dispatch_once() en lugar de OSAtomic *. Menos quisquilloso, igual de rápido o más rápido. –