2009-07-18 10 views
7

Ho una classe che ho trasformato in un singleton e sono in grado di salvare il suo stato utilizzando un NSKeyedArchiver, ma non riesco a capirli tirandone indietro lo stato.Caricamento di uno stato Singleton da NSKeyedArchiver

Nella funzione che fa il carico ho

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

Con questo codice, che cosa decodeObjectForKey tornare? In realtà non può essere un'altra istanza di Venue e non viene caricata in nessuno dei valori salvati. Prima di convertirlo in un singleton, il salvataggio e il caricamento funzionavano bene.

risposta

8

Qui è dove penso che questo sta andando male . Hai familiarità con NSCoding e in genere lo adotti rendendo il tuo oggetto codificabile tramite encodeWithCoder: e initWithCoder: sovrascrive. Ciò che renderà tutto più semplice è che puoi ancora utilizzare NSCoding e NSCoders senza sovrascrivere questi metodi. È possibile codificare lo stato dell'oggetto condiviso senza codificare l'oggetto condiviso stesso. Ciò impedisce la domanda scomoda di decodificare l'oggetto condiviso.

Ecco un esempio di quello che penso che si possa fare:

@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 

Qui abbiamo un oggetto condiviso, e utilizza archivio con chiave a persistere la configurazione, ma non codificano l'oggetto condiviso in sé. Ciò evita la questione scomoda di decodificare una seconda istanza della classe Singleton.

+0

Questa risposta ha un po 'più senso per me, solo perché riesco a capirlo. L'init di altri sembra corretto ma un po 'più difficile da concettualizzare. – rob5408

+0

Ottima risposta ... solo una FYI nel caso in cui qualcuno si imbattesse in questa domanda, credo che il metodo init dovrebbe usare un NSKeyedUnarchiver. – Bern11

+0

Grazie, aggiornato lo snippet. –

3

È necessario eseguire il caricamento nel singleton stesso cosa sta succedendo qui è creare il singolo, assegnare un lval al singleton, quindi creare un nuovo oggetto e riassegnare il lval a quel nuovo oggetto SENZA modificare il valore singleton. In altre parole:

//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]; 

Ciò che si vuole fare è gestirlo in SharedVenue. Ci sono un paio di modi le persone fanno single, quindi non posso essere sicuro di quello che state facendo, ma lascia supporre sharedVenue attualmente simile a questa:

static Venue *gSharedVenue = nil; 

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

    return gSharedVenue; 
} 

Partendo dal presupposto che è il caso che si desidera cambiarlo in caricare l'oggetto nel supporto globale 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; 
} 

Ovviamente è necessario trasmettere in qualche modo il percorso effettivo al file oggetto archiviato.

modifica in base ai COMMENTO:

Va bene, se si utilizza l'alloc Singleton base è necessario per affrontare questo nelle classi metodo 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, penso di capirlo, lasciatemi fare un tentativo. Per quanto riguarda il modo in cui sto implementando il mio singleton, sto usando la macro di questo ragazzo che è davvero fantastica: http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html – rob5408

+0

He usa un modello diverso, è necessario gestirlo nel metodo init dei singleton. Devi fare una cosa complicata, che è restituire una cosa non super, sto aggiungendo come lo fai alla risposta –

Problemi correlati