2012-04-08 22 views
5

He encontrado ese patrón de singleton en la red. Me parece que tiene muchas cosas que se pueden optimizar.Objective-C - ¿Optimizar este patrón singleton?

-In sharedMySingleton método, no es necesario llamar a retener? No estoy seguro ...
-Si no, ¿por qué hay una retención en allocWithZone?
-cual es el uso de @synchronized. Los NSAssert hacen pensar que se puede llamar al bloque muchas veces, por lo que, en caso afirmativo, debería haber más código para liberar la memoria anterior, o salir del bloque claramente sin solo insertar NSA, y en caso negativo, ¿por qué hay NSAssert?
-la cadena comprendida entre sharedMySingleton y alloc parece extraña. Yo escribí mi mismo algo así como:

+(MySingleton*)sharedMySingleton 
{ 
    @synchronized([MySingleton class]) 
    { 
     if (_sharedMySingleton == nil) _sharedMySingleton = [[self alloc] init]; 
     return _sharedMySingleton; 
    } 

    return nil; 
} 

+(id)alloc 
{ 
    @synchronized([MySingleton class]) 
    { 
     return [super alloc]; 
    } 

    return nil; 
} 

patrón Singleton

#import "MySingleton.h" 

@implementation MySingleton 

// ########################################################################################################## 
// ######################################## SINGLETON PART ################################################## 
// ########################################################################################################## 
static MySingleton* _sharedMySingleton = nil; 

// ================================================================================================= 
+(MySingleton*)sharedMySingleton 
// ================================================================================================= 
{ 
    @synchronized([MySingleton class]) 
    { 
     if (_sharedMySingleton == nil) [[self alloc] init]; 
     return _sharedMySingleton; 
    } 

    return nil; 
} 

// ================================================================================================= 
+(id)alloc 
// ================================================================================================= 
{ 
    @synchronized([MySingleton class]) 
    { 
     NSAssert(_sharedMySingleton == nil, @"Attempted to allocate a second instance of a singleton."); 
     _sharedMySingleton = [super alloc]; 
     return _sharedMySingleton; 
    } 

    return nil; 
} 

+ (id)allocWithZone:(NSZone *)zone { return [[self sharedMySingleton] retain]; } 
- (id)copyWithZone:(NSZone *)zone { return self; } 
- (id)retain      { return self; } 
- (NSUInteger)retainCount   { return NSUIntegerMax; /* denotes an object that cannot be released */} 
- (oneway void)release    { /* do nothing */ } 
- (id)autorelease     { return self; } 

// ########################################################################################################## 
// ########################################################################################################## 
// ########################################################################################################## 

// ================================================================================================= 
-(id)init 
// ================================================================================================= 
{ 
    if (!(self = [super init])) return nil; 

    return self; 
} 

// ================================================================================================= 
-(void) dealloc 
// ================================================================================================= 
{ 
    [super dealloc]; 
} 

// ================================================================================================= 
-(void)test 
// ================================================================================================= 
{ 
    NSLog(@"Hello World!"); 
} 

@end 

Respuesta

17

Usted no debe utilizar este modelo en absoluto (que es un caso muy especial de Singleton que casi nunca necesita, e incluso en ese caso, generalmente no deberías usarlo).

Hay muchos buenos patrones discutidos en What should my Objective-C singleton look like?, pero la mayoría de ellos están obsoletos desde el lanzamiento de GCD. En las versiones modernas de Mac y iOS, se debe utilizar el siguiente patrón, dada por Colin Barrett en la cuestión vinculada:

+ (MyFoo *)sharedFoo 
{ 
    static dispatch_once_t once; 
    static MyFoo *sharedFoo; 
    dispatch_once(&once, ^{ sharedFoo = [[self alloc] init]; }); 
    return sharedFoo; 
} 

sólo copiar aquí en lugar de marcar la pregunta duplicados porque las respuestas de mayor audiencia de la vieja pregunta están desactualizados

+0

¿Qué pasa con los otros métodos: retener, retener cuenta, liberar, copiarWithZone, ...? ¿Por qué estás hablando de versiones "modernas"? ¿Qué quieres decir con moderno? ¿Alguna idea de por qué Apple no actualizó su fragmento singleton con este tipo de código? – Oliver

+1

Por moderno, quiero decir desde la introducción de GCD. No debe anular estos métodos. El único código de muestra que muestra una anulación de estos está aquí: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html Como se observa, esta es una implementación * strict * de singleton, que el texto anterior explica generalmente no es necesario. Abrí un doc case para mejorar esta documentación, ya que confunde a muchos desarrolladores. Mike Ash tiene una buena publicación sobre esto: http://www.mikeash.com/pyblog/friday-qa-2009-10-02-care-and-feeding-of-singletons.html –

+0

¿Cuál es el equivalente a 10.6 en términos de iOS? – Oliver

0

No hay necesidad de llamar al retain porque hay un alloc. Llame al retain además de eso podría causar una pérdida de memoria. Se conserva en allocWithZone porque es un singleton, por lo que no queremos crear dos instancias diferentes de la clase. Al asignar una nueva instancia, aumentamos el conteo retenido de la instancia singleton. ¿Porqué es eso? Probablemente para evitar que alguien no se dé cuenta del tipo de singleton de la clase. Si llama al allocWithZone y luego libera la instancia, todo sigue funcionando bien, y realmente accedió a la instancia de singleton compartida.

@synchronized se usa para evitar que dos llamadas de dos hilos diferentes entren en la instrucción if al mismo tiempo. Entonces el código es seguro para subprocesos.

El NSSAssert es probablemente para hacer que la aplicación 'falle' si alguna vez se crean dos instancias del singleton. Es el código '' solo para estar seguro '', también llamado programación defensiva.

En cuanto a la cadena entre sharedMySingleton y alloc, creo que está bien.

0

En el método sharedMySingleton, no hay necesidad de llamar a retener?

alloc devuelve una nueva instancia con un recuento de referencia de uno, por lo que no es necesario retener.

Si no, ¿por qué hay un retener en allocWithZone?

Según las reglas, cuando se llama allocWithZone, es el propietario de la referencia, por lo tanto, allocWithZone aumento mucho el recuento de referencia para usted. Pero esta implementación de allocWithZone devuelve la instancia de singleton que ya fue creada y es propiedad de otra persona (el método sharedMySingleton). Por lo tanto, el método sharedMySingleton crea el objeto con alloc, por lo tanto, se convierte en propietario. Y luego obtiene la misma instancia a través de allocWithZone, por lo tanto, se convierte en un segundo propietario de la misma instancia. Por lo tanto, el conteo retenido debe aumentar ya que hay dos propietarios ahora. Es por eso que allocWithZone necesita retener.

¿Cuál es el uso de @synchronized?

@synchronized permite llamar el código simultáneamente por varios hilos. Si nunca llama al sharedMySingleton desde más de un hilo, entonces no es necesario y puede omitirlo.

El NSAssert hacen pensar que el bloque puede ser llamado muchas veces, por lo que si sí, debe haber algo más de código para liberar la memoria anterior o salida del bloque claramente sin apenas NSAsserting, y si no, ¿por qué es existe este NSAssert?

Como la clase está destinada a ser singleton, alloc solo se debe llamar una vez. El NSAssert() finaliza el programa si se llama al alloc más de una vez. Como NSAssert() finaliza el programa, cuando se llama al alloc por segunda vez, no es necesaria la gestión de la memoria.