2009-07-18 12 views
7

Tengo una clase que he convertido en un singleton y puedo guardar su estado usando un NSKeyedArchiver, pero no puedo ajustar mi estado para volver a sacarlo.Cargando el estado de Singleton desde NSKeyedArchiver

En la función que hace la carga Tengo

Venue *venue = [Venue sharedVenue]; 
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]]; 
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 
venue = [unarchiver decodeObjectForKey:@"Venue"]; 
[unarchiver finishDecoding]; 

Con este código, lo que hace decodeObjectForKey volver? Realmente no puede ser otra instancia de Venue y no se está cargando en ninguno de los valores guardados. Antes de convertirlo a singleton, guardar y cargar funcionaba bien.

Respuesta

8

Aquí es donde creo que esto va mal . Está familiarizado con NSCoding y, por lo general, lo adopta haciendo que su objeto sea codificable mediante encodeWithCoder: y initWithCoder: anula. Lo que simplificará todo esto es que aún puede usar NSCoding y NSCoders sin anular esos métodos. Puede codificar el estado del objeto compartido sin codificar el objeto compartido en sí mismo. Esto evita la incómoda cuestión de decodificar el objeto compartido.

He aquí un ejemplo de lo que creo que se podría hacer:

@implementation MySharedObject 
+ (id)sharedInstance { 
    static id sharedInstance = nil; 
    if (!sharedInstance) { 
     sharedInstance = [[MyClass alloc] init]; 
    } 
} 

- (id)init { 
    if ((self = [super init])) { 
     NSData *data = /* probably from user defaults */ 
     if (data) { // Might not have any initial state. 
      NSKeyedUnarchiver *coder = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease]; 
      myBoolean = [coder decodeBoolForKey:@"MyBoolean"]; 
      myObject = [[coder decodeObjectForKey:@"MyObject"] retain]; 
      [coder finishDecoding]; 
     } 
    } 
    return self; 
} 

- (void)saveState { 
    NSMutableData *data = [NSMutableData data]; 
    NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease]; 
    [coder encodeBool:myBoolean forKey:@"MyBoolean"]; 
    [coder encodeObject:myObject forKey:@"MyObject"]; 
    [coder finishEncoding] 
    // Save the data somewhere, probably user defaults... 
} 
@end 

Aquí tenemos un objeto compartido, y se utiliza con llave archivo de configuración de persistir, pero que no codifican el objeto compartido en sí. Esto evita la incómoda cuestión de decodificar una segunda instancia de la clase singleton.

+0

Esta respuesta tiene más sentido para mí, solo porque puedo entenderlo. El init del otro parece correcto pero un poco más difícil de conceptualizar. – rob5408

+0

Gran respuesta ... solo un FYI en caso de que alguien se tropiece con esta pregunta, creo que el método init debería usar un NSKeyedUnarchiver. – Bern11

+0

Gracias, actualicé el fragmento. –

3

Tiene que cargar solo en el singleton. Lo que está pasando aquí es que cree el single, asigne un lval al singleton, luego cree un nuevo objeto y reasigne el lval a ese nuevo objeto SIN modificar el semifallo. En otras palabras:

//Set venue to point to singleton 
Venue *venue = [Venue sharedVenue]; 

//Set venue2 to point to singleton 
Venue *venue2 = [Venue sharedVenue]; 

NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]]; 
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 

//Set venue to unarchived object (does not change the singleton or venue2) 
venue = [unarchiver decodeObjectForKey:@"Venue"]; 
[unarchiver finishDecoding]; 

Lo que quieres hacer es solucionar esto en sharedVenue. Hay un par de maneras que la gente hace únicos, por lo que no pueden estar seguros de lo que está haciendo, pero supongamos sharedVenue actualmente se ve algo como esto:

static Venue *gSharedVenue = nil; 

- (Venue *) sharedVenue { 
    if (!gSharedVenue) { 
    gSharedVenue = [[Venue alloc] init]; 
    } 

    return gSharedVenue; 
} 

Si se asume que es el caso de que quiera cambiarlo a cargar el objeto en el respaldo mundial singleton:

static Venue *gSharedVenue = nil; 

- (Venue *) sharedVenue { 
    if (!gSharedVenue) { 
    NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]]; 
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 
    [data release]; 

    gSharedVenue = [unarchiver decodeObjectForKey:@"Venue"]; 
    [unarchiver finishDecoding]; 
    [unarchiver release]; 
    } 

    if (!gSharedVenue) { 
    gSharedVenue = [[Venue alloc] init]; 
    } 

    return gSharedVenue; 
} 

Obviamente se necesita para transmitir de alguna manera la trayectoria real de archivo de objetos archivados.

la edición en función a un comentario:

bien, si está utilizando el singleton basado alloc que necesita para hacer frente a esto en las clases método init:

- (id) init { 
    self = [super init]; 

    if (self) { 
    NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]]; 
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 
    [data release]; 

    Venue *storedVenue = [unarchiver decodeObjectForKey:@"Venue"]; 
    [unarchiver finishDecoding]; 
    [unarchiver release]; 

    if (storeVenue) { 
     [self release]; 
     self = [storedVenue retain]; 
    } 

    } 

    return self; 
} 
+0

Ok, creo que entiendo eso, déjame darle una oportunidad. En cuanto a la forma en que estoy implementando mi singleton, estoy usando la macro de este tipo que es bastante impresionante: http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html – rob5408

+0

He usa un patrón diferente, necesita manejar esto en el método init de su singleton. Tienes que hacer una cosa complicada, que es devolver algo que no es súper, agrego cómo lo haces a la respuesta –

Cuestiones relacionadas