5

che sto memorizzare una serie di dati in un file plist (nella cartella documenti di candidatura), ed è strutturato in questo modo:Qual è il modo migliore per archiviare e recuperare gli NSMutableArray multidimensionali?

Dictionary { 
    "description" = "String Value", 
    "sections" = Array (
     Array (
      Number, 
      ... 
      Number 
     ), 
     Array (
      Number, 
      ... 
      Number 
     ) 
    ), 
    "items" = Array (
     Array (
      Number, 
      ... 
      Number 
     ), 
     Array (
      Number, 
      ... 
      Number 
     ) 
    ) 
} 

Se ho appena recuperare con
NSMutableDictionary *d = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile] non lo farò essere in grado di sostituire gli oggetti numerici, correggere? Quindi sto ricorsivamente attraverso i dati in questo momento e formando una versione mutevole dell'intera cosa, e ha funzionato in un caso, ma ora mi sta dicendo mutating method sent to immutable object quando l'intera cosa è mutabile.

C'è un modo più semplice/migliore per farlo? Se fa la differenza, i miei dati sono solo numeri interi e booleani.

+0

Puoi darci un po 'più di informazioni su quale oggetto viene inviato questo metodo, ed eventualmente postare il codice che usi per formare la nuova versione e quello con cui viene generato l'errore di runtime? –

+0

Stai anche facendo delle copie mutabili degli array prima di iniziare a modificare i valori? – vfn

risposta

9

Invece di scrivere tutto ciò junk di classe personalizzata, è necessario utilizzare NSPropertyListSerialization. In particolare, vedere il metodo propertyListWithData:options:format:error:. Utilizzo Esempio:

NSMutableDictionary *d = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfFile:@"path/to/file"] 
                    options:NSPropertyListMutableContainers 
                    format:NULL 
                    error:NULL]; 

Questo renderà tutti i contenitori mutevole, ma mantenere i nodi foglia (per esempio NSStrings) immutabili. C'è anche un'opzione per rendere mutevoli anche le foglie.

+0

+1 Questo è intrigante.La documentazione di Apple afferma che il parametro 'options' non deve essere usato durante la lettura (come mostrato qui), ma può, in effetti, essere usato quando * scrive * il file plist, in modo che i dati, quando letti, siano mutabili. Vedere http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSPropertyListSerialization_Class/Reference/Reference.html –

+0

Vorrei ancora discutere per una classe personalizzata al fine di ottenere i benefici OO di proprietà accessors e incapsulare i metodi di lettura e scrittura, ma questa tecnica sarà sicuramente di aiuto in quelle occasioni in cui una semplice lettura-modifica-scrittura è in ordine. Grandi cose! –

+0

Sì, ho seguito la rotta personalizzata per comodità, ma questo sembra abbastanza semplice. Grazie per l'input. – kbanman

8

Generalmente trovo più semplice creare una o più classi personalizzate per gestire il caricamento e il salvataggio. Questo consente di convertire gli array a mutableArrays esplicitamente:

MyThing.h

@interface MyThing : NSObject 
{ 
    NSString * description; 
    NSMutableArray * sections; 
    NSMutableArray * items; 
} 
@property (copy) NSString * description; 
@property (readonly) NSMutableArray * sections; 
@property (readonly) NSMutableArray * items; 
- (void)loadFromFile:(NSString *)path; 
- (void)saveToFile:(NSString *)path; 
@end 

MyThing.m

@implementation MyThing 
@synthesize description; 
@synthesize sections 
@synthesize items; 

- (id)init { 
    if ((self = [super init]) == nil) { return nil; } 
    sections = [[NSMutableArray alloc] initWithCapacity:0]; 
    items = [[NSMutableArray alloc] initWithCapacity:0]; 
    return self; 
} 

- (void)dealloc { 
    [items release]; 
    [sections release]; 
} 

- (void)loadFromFile:(NSString *)path { 
    NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile:path]; 
    [self setDescription:[dict objectForKey:@"description"]]; 
    [sections removeAllObjects]; 
    [sections addObjectsFromArray:[dict objectForKey:@"sections"]]; 
    [items removeAllObjects]; 
    [items addObjectsFromArray:[dict objectForKey:@"items"]]; 
} 

- (void)saveToFile:(NSString *)path { 
    NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys: 
          description, @"description", 
          sections, @"sections", 
          items, @"items", 
          nil]; 
    [dict writeToFile:path atomically:YES]; 
} 

@end; 

Con questo fatto, è possibile incapsulare tutti i codici di imballaggio e decompressione nei metodi loadFromFile e saveToFile. Il vantaggio principale di questo approccio è che il programma principale diventa molto più semplice, e permette di accedere gli elementi della vostra struttura dati come proprietà:

MyThing * thing = [[MyThing alloc] init]; 
[thing loadFromFile:@"..."]; 
... 
thing.description = @"new description"; 
[thing.sections addObject:someObject]; 
[thing.items removeObjectAtIndex:4]; 
... 
[thing saveToFile:@"..."]; 
[thing release]; 
+0

Wow, questa è la risposta più profonda, signore :) Questo ha molto senso, non ho idea del perché non mi sia passato per la testa. Grazie mille! – kbanman

1

Ciò che si vuole è una copia mutabile profonda. Il cacao non include un modo per farlo. Alcune persone hanno già scritto tali implementazioni di copia profonda (example).

Tuttavia, Core Foundation include lo CFPropertyList API, che supporta sia la creazione di profonde copie mutabili di oggetti di elenchi di proprietà sia la lettura negli elenchi di proprietà dal disco come tipi di dati mutabili. (E, naturalmente, i tipi di elenchi di proprietà di Core Foundation sono collegati tra loro da Cocoa, il che significa che non devi convertire tra di essi: un NSArray è un CFArray e viceversa.)