2010-01-22 5 views
6

Recentemente ho giocato con il codice per un'app per iPhone per analizzare XML. Attaccando a Cocoa, ho deciso di andare con la classe NSXMLParser. L'app sarà responsabile per l'analisi di oltre 10.000 "computer", tutti contenenti 6 altre stringhe di informazioni. Per il mio test, ho verificato che l'XML ha una dimensione di 900k-1MB.NSXMLParser Memory Allocation Efficienza per l'iPhone

Il mio modello di dati è quello di mantenere ogni computer in un NSDictionary hash da un identificatore univoco. Ogni computer è anche rappresentato da un NSDictionary con le informazioni. Così alla fine della giornata, finisco con un NSDictionary contenente 10k altri NSDictionaries.

Il problema che sto incontrando non riguarda la perdita di memoria o l'efficiente archiviazione della struttura dati. Al termine del mio parser, la quantità totale di oggetti allocati aumenta solo di circa 1 MB. Il problema è che mentre NSXMLParser è in esecuzione, la mia allocazione di oggetti sta salendo fino a 13 MB. Potrei capire 2 (uno per l'oggetto che sto creando e uno per il raw NSData) più un po 'di spazio per lavorare, ma 13 sembra un po' alto. Non riesco a immaginare che NSXMLParser sia inefficiente. Pensieri?

Codice ...

Il codice per avviare l'analisi ...

NSXMLParser *parser = [[NSXMLParser alloc] initWithData: data]; 
[parser setDelegate:dictParser]; 
[parser parse]; 
output = [[dictParser returnDictionary] retain];   
[parser release]; 
[dictParser release]; 

E il codice delegato del parser ...

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict { 

    if(mutableString) 
    { 
     [mutableString release]; 
     mutableString = nil; 

    } 

    mutableString = [[NSMutableString alloc] init];  

} 

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 
    if(self.mutableString) 
    { 

     [self.mutableString appendString:string]; 

    } 
} 

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { 

    if([elementName isEqualToString:@"size"]){ 
     //The initial key, tells me how many computers 
     returnDictionary = [[NSMutableDictionary alloc] initWithCapacity:[mutableString intValue]]; 
} 

    if([elementName isEqualToString:hashBy]){ 
    //The unique identifier 
     if(mutableDictionary){ 
      [mutableDictionary release]; 
      mutableDictionary = nil; 
    }  

     mutableDictionary = [[NSMutableDictionary alloc] initWithCapacity:6]; 

     [returnDictionary setObject:[NSDictionary dictionaryWithDictionary:mutableDictionary] forKey:[NSMutableString stringWithString:mutableString]]; 
} 

    if([fields containsObject:elementName]){ 
     //Any of the elements from a single computer that I am looking for 
     [mutableDictionary setObject:mutableString forKey:elementName]; 
} 
} 

Tutto inizializzato e rilasciato in modo corretto. Di nuovo, non ricevo errori o perdite. Semplicemente inefficiente.

Grazie per qualsiasi pensiero!

+0

mostra parte del file xml – vaddieg

risposta

3

Non si può dire nulla di specifico sul proprio codice, ma dare un'occhiata all'esempio di Apple XMLPerformance - confronta le prestazioni di NSXMLParser e libxml - i risultati sono decisamente a favore di quest'ultimo. In uno dei miei progetti il ​​passaggio da NSXMLParser a libxml ha dato un notevole incremento delle prestazioni, quindi suggerirei di utilizzarlo.

+0

Il controllo di libxml analizza su SSL? Solo con una rapida ricerca non ero in grado di trovare molto su di esso. Se non può, allora è un affare per me. – Staros

0

ho usato NSXMLParser per analizzare i file XML con circa 500 registrazioni a 700K o giù di lì. Ho trovato questo era nella parte superiore del limite di memoria iPhone 3G. La memoria si espandeva molto più della dimensione del file XML, raggiungendo a volte 15 MB. Il problema era che stavo conservando i record in un array, quindi entrambi erano in memoria allo stesso tempo. Durante l'analisi, la memoria finita è di nuovo scesa, ma se mai ha raggiunto 15 o 20 MB, l'app si blocca. Si suppone che libxml sia molto più efficiente in termini di memoria.

Si potrebbe anche provare a memorizzare gli oggetti creati con Core Data anziché in una matrice. Core Data si occupa della memoria più deallocando gli oggetti quando non sono necessari.

Con la mia app, ho ridotto il sovraccarico della memoria ottimizzando altre parti, in modo che la memoria totale utilizzata mai raggiunto il limite superiore.

6

NSXMLParser è un ingordo di memoria:

  1. non è un vero e proprio parser streaming: initWithURL: scaricherà la piena xml prima di elaborarlo. Per la memoria utilizzare questo è male come deve allocare la memoria per la piena XML goduto non può essere recuperato fino alla fine del parse.Per le prestazioni è anche il male, in quanto non è possibile interconnettere la parte intensiva di IO del download e parte intensiva della CPU di analisi.
  2. non rilascerà memoria. Sembra che che gli stringhe/dizionari creati durante l'analisi siano tenuti intorno allo fino alla fine del parse. Ho provato per migliorarlo con l'uso creativo di NSAutoreleasePool ma senza successo .

alternative sono libxml e AQXMLParser che è un involucro compatibile NSXMLParser intorno libxml, o ObjectiveXML.

Vedere my blog article per maggiori dettagli.

+0

Ahh, questo spiega perché questo post sembra funzionare: passare a initWithData sembra funzionare: http://blog.filipekberg.se/2010/11/30/nsxmlparser-has-memory-leaks-in-ios-4/ – PostCodeism

0

Se si desidera sapere dove sta andando la memoria, eseguire il codice in Strumenti utilizzando il modello ObjectAlloc e ordinare l'elenco di classi in base alla dimensione totale. Una volta che l'utilizzo complessivo della memoria diventa enorme, vedrai una classe o alcune classi come i più grandi occupanti della memoria.

Poi, drill-down in una di queste classi ed esaminare le istanze di esso per vedere che cosa li ha creati.

Quindi il conosce, dalle prove, in cui si trova il problema.

0

Basta passare a libxml.

po 'di mal di testa, ma il link Vladimir registrato è stato un grande aiuto.

Ora il rigonfiamento per un file 900k - 1mb è solo di circa 2-3mb. Inoltre, poiché è un parser di streaming, viene eseguito quasi immediatamente dopo i resi NSURLRequest.

risposta finale - libxml.

Grazie per il vostro aiuto ragazzi!

0

Se si sta cercando un sostituto per NSXMLParser in grado di gestire lo streaming di documenti XML di grandi dimensioni su http, potrebbe essere interessato al mio Expat Objective C Wrapper.

0

Ho usato AQXMLParser prima, ed è sicuramente molto più efficiente della memoria di NSXMLParser.