2010-01-22 11 views
6

Recientemente he estado jugando con el código de una aplicación de iPhone para analizar XML. Siguiendo con Cocoa, decidí ir con la clase NSXMLParser. La aplicación será responsable de analizar más de 10,000 "computadoras", todas las cuales contienen otras 6 cadenas de información. Para mi prueba, he verificado que el XML tiene un tamaño aproximado de 900k-1MB.NSXMLParser Eficiencia de asignación de memoria para el iPhone

Mi modelo de datos es para mantener cada computadora en un hash NSDictionary con un identificador único. Cada computadora también está representada por un NSDiccionario con la información. Entonces, al final del día, termino con un NSDictionary que contiene 10k otros NSDictionaries.

El problema con el que me estoy encontrando no es sobre la fuga de memoria o el almacenamiento eficiente de la estructura de datos. Cuando mi analizador está listo, la cantidad total de objetos asignados solo aumenta en aproximadamente 1 MB. El problema es que mientras NSXMLParser se está ejecutando, mi asignación de objetos está aumentando tanto como 13 MB. Pude entender 2 (uno para el objeto que estoy creando y otro para NSData en bruto) más un pequeño espacio para trabajar, pero 13 parece un poco alto. No puedo imaginar que NSXMLParser sea tan ineficiente. ¿Pensamientos?

Código ...

El código para iniciar el análisis ...

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

Y el código delegado del analizador ...

-(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]; 
} 
} 

Todo inicializado correctamente y puesto en libertad. De nuevo, no estoy recibiendo errores o goteando. Simplemente ineficiente.

Gracias por cualquier idea!

+0

muestra parte de su xml – vaddieg

Respuesta

3

No se puede decir nada específico acerca de su código, pero eche un vistazo a la muestra de XMLPerformance de Apple - compara NSXMLParser y el rendimiento de libxml - definitivamente los resultados están a favor de este último. En uno de mis proyectos, el cambio de NSXMLParser a libxml dio un gran impulso al rendimiento, por lo que sugiero que lo use.

+0

¿Analiza libxml handel sobre SSL? Solo con una búsqueda rápida no pude encontrar mucho sobre eso. Si no puede, entonces eso es un factor decisivo para mí. – Staros

0

He usado NSXMLParser para analizar archivos XML con alrededor de 500 registros a 700K o menos. Encontré esto en el límite superior del límite de memoria de iPhone 3G. La memoria se expandió a mucho más que el tamaño del archivo XML, llegando a 15 MB a veces. El problema era que estaba almacenando los registros en una matriz, por lo que ambos estaban en la memoria al mismo tiempo. Cuando el análisis finalizó, la memoria volvió a bajar, pero si llegaba a alcanzar 15 o 20 MB, la aplicación se bloqueaba. Se supone que libxml es mucho más eficiente con la memoria.

También puede tratar de almacenar los objetos creados con datos principales en lugar de en una matriz. Core Data se ocupa más de la memoria al desasignar objetos cuando no son necesarios.

Con mi aplicación, reduje la sobrecarga de memoria optimizando otras partes, de modo que la memoria total utilizada nunca alcanzó el límite superior.

6

NSXMLParser es un cerdo de la memoria:

  1. no es un verdadero analizador en tiempo real: initWithURL: descargará el XML completo antes de procesarlo. Para la memoria use esto es malo ya que tiene que asignar la memoria para el xml completo que no se puede reclamar hasta el final del análisis .Para el rendimiento es también malo, ya que no se puede intercalar la parte IO intensiva de la descarga de y parte intensiva de la CPU de análisis.
  2. no liberará la memoria. Parece que las cadenas/diccionarios creados durante el análisis se mantienen alrededor de hasta el final del análisis. He intentado para mejorarlo con el uso creativo de NSAutoreleasePool pero sin ningún éxito.

alternativas son libxml y AQXMLParser que es una envoltura compatible NSXMLParser alrededor libxml, o ObjectiveXML.

Ver my blog article para más detalles.

+0

Ahh, eso explica por qué esta publicación pareció funcionar; parece que funciona el cambio a initWithData: http://blog.filipekberg.se/2010/11/30/nsxmlparser-has-memory-leaks-in-ios-4/ – PostCodeism

0

Si desea saber a dónde va su memoria, ejecute el código en Instrumentos utilizando la plantilla ObjectAlloc, y ordene la lista de clases por tamaño total. Una vez que el uso general de la memoria sea enorme, verá una clase o unas pocas clases como el mayor ocupante (s) de la memoria.

Luego, profundice en una de estas clases y examine las instancias para ver qué las creó.

Entonces usted sabe, de la evidencia, donde reside su problema.

0

Acaba de cambiar a libxml.

Un poco de dolor de cabeza pero el enlace que Vladimir publicó fue de gran ayuda.

Ahora la hinchazón para un archivo de 900k - 1mb es solo alrededor de 2-3mb. Además, debido a que es un analizador de transmisión en tiempo real, se realiza casi inmediatamente después de la devolución de NSURLRequest.

Respuesta final - libxml.

Gracias por toda su ayuda, muchachos!

0

Si está buscando un reemplazo para NSXMLParser que puede manejar la transmisión de documentos XML de gran tamaño en http, podría estar interesado en mi Expat Objective C Wrapper.

0

He usado AQXMLParser antes, y es definitivamente mucho más eficiente de memoria que NSXMLParser.