2012-09-06 34 views
9

Tengo este problema. Tengo una base de datos de imágenes en Core Data. Cojo todas las imágenes (aproximadamente 80 MB) y las coloco en un NSMutableArray. Los objetos están correctamente criticada:Uso de memoria de datos centrales y advertencia de memoria

NSArray *fetchResults = [self.managedObjectContext executeFetchRequest:request error:&error]; 
self.cache = [NSMutableArray arrayWithArray:fetchResults]; 
for (ImageCache *imageObject in self.cache) { 
    NSLog(@"Is fault? %i", [imageObject isFault]); 
} 

lectura del registro, veo que los objetos están correctamente criticada Sin embargo, el uso de instrumentos, veo que se usan 80 MB de memoria. Creo que esta es la razón por la que Core Data almacena en caché sus resultados, y debería liberar la memoria cuando sea necesaria. Pero (y este es mi "problema"), si simulo una advertencia de memoria, ¡no pasa nada! Los 80MB permanecen allí.

En cuanto a los instrumentos - asignaciones, el 80 MB se utilizan por muchos Malloc: (ejemplo)

Gráfico Categoría vivo Bytes # # Vida Transitorias general Bytes # # En general Asignaciones (neto/total) 0 Malloc 176,00 KB 8,59 MB 50 57 18,39 MB 107% 0.00,% 0.00 0 Malloc 200,00 KB 8,20 MB 42 460 98,05 MB 502% 0.00,% 0.04 0 Malloc 168,00 KB 7,05 MB 43 MB 19 10,17 62%: 0,00, 0,00%

Este es un enlace a una imagen de todo el árbol de llamadas: https://www.dropbox.com/s/du1b5a5wooif4w7/Call%20Tree.png

¿Alguna idea? Gracias

+0

¿Quizás Core Data libera memoria en 'Memory Warning Level 2'? ¿Es posible producir una falla de memoria baja con su escenario? – brigadir

+0

¿Hay algún "método mágico" para simular un nivel de advertencia de memoria 2? O "simplemente" tengo que consumir memoria? – LombaX

+0

No conozco ningún método de simulación. Debería ejecutar otra aplicación "pesada" (Appstore por ejemplo), mantener su aplicación en segundo plano y seguir el registro de la consola y la tabla de memoria de Instrumentos. La advertencia de "nivel 2" se mencionará en la consola, por lo que debe mirar la tabla de memoria en ese momento. – brigadir

Respuesta

9

Ok, he entendido por qué sucede. Cuando realiza una solicitud de recuperación para una entidad, incluso si la falla está habilitada, TODOS LOS DATOS de esa entidad se cargan en la memoria. Incluyendo grandes datos binarios. Puede resolver esto utilizando muchos métodos:

1- configurándolo en su NSFetchRequest: [request setIncludesPropertyValues:NO]; en NO, los datos no se cargan en la memoria caché de inmediato, pero sólo a petición (cuando se accede a la propiedad y la culpa es despedido) Pero esto tiene un "problema". Incluso si intenta volver a fallar la propiedad (porque no lo necesita de inmediato y desea liberar la memoria, usando [self.managedObjectContext refreshObject:object mergeChanges:NO];), la memoria no se libera. La caché permanece activa hasta que se restablece managedObjectContext.

Esto es mejor:

2- se puede dividir los datos en entidades separadas. En mi caso, solo tenía 2 propiedades: una url y datos de imagen. Dividí los datos en 2 entidades con una relación 1: 1: imagecache e imagedata. Realizado un fetchRequest para toda la fila de la entidad "imagecache" (con la propiedad url), y al igual que la solución anterior, no se almacenó en memoria ninguna memoria. El propery imagecache.relationship.image fue fallado correctamente. El acceso a esta propiedad provocó que se disparara la falla y se llenara la memoria caché. Pero en este caso, al hacer [self.managedObjectContext refreshObject:object mergeChanges:NO]; en el objeto "caché de imagen" (el objeto "padre"), se obtuvo inmediatamente la liberación de la caché y la memoria, volviendo a fallar la propiedad imagecache.relationship.image. Atención: no haga en el objeto "hijo", si lo hace [self.managedObjectContext refreshObject:object.relationship mergeChanges:NO], por alguna razón no se libera la caché. Creo que es por eso que atraviesas la relación.

3- Dije que esto era principalmente una pregunta académica, la verdadera solución de "todo el día" (mejor rendimiento y menos dolor de cabeza) para estos problemas es evitar el almacenamiento de grandes volúmenes de datos dentro de la base de datos central. Puede guardar sus datos como archivos y almacenar solo una referencia (ruta de archivo) o, con iOS 5, tiene la posibilidad de configurar "usar almacenamiento externo" en cualquier propiedad de "Datos" dentro de su modelo de datos principal. Esto hará todo el trabajo por ti.

+1

Oye, me pregunto si alguna vez encontraste alguna otra solución a este problema. Desafortunadamente, no tengo ningún gran objeto de memoria NSData para el que pueda usar la solución 3. En cambio, tengo cientos de miles de objetos que se muestran como anotaciones en el mapa. Busco en lotes, así que me aseguro de que si el dispositivo no tiene suficiente memoria, puedo limitar la cantidad de datos que se muestran. Pero cuando recibo una advertencia de memoria y llamo refreshObject: mergeChanges: la memoria no se ve afectada como usted mencionó, y termino recibiendo un bloqueo de memoria. ¿Alguna idea? – horsejockey

0

Creo que deberías cargar menos objetos en la memoria por lotes.

memoria liberada por coredata ocurre detrás de las escenas y no tiene que programar para ello; la mala noticia es que ocurre detrás de escena y, por lo tanto, puede "mágicamente" masticar la memoria.

Las formas a su alrededor son muchas; por ejemplo, use un predicado para seleccionar solo las filas que absolutamente necesita; no haga una llamada general para buscar todo y luego vaya por la lista uno por uno. Lo más probable es que se bloquee cuando realiza la llamada general y CoreData intenta cargar todos los objetos.

+0

Sí, la primera solución que apliqué fue para obtener solo los datos que necesitaba. Esta es principalmente una pregunta "académica". La documentación es clara al respecto, la administración del caché está hecha por Core Data y todo sucede detrás de la escena, pero incluso dice que en el caso de poca memoria, la memoria es gratuita. Esperaba ver una disminución de la memoria utilizada por los datos centrales después de una advertencia de memoria. ¡Al no ver esto, estaba pensando que había algo mal en mi código ...! Me gustaría ver "con los ojos" Core Data liberar memoria en situaciones de poca memoria :-) – LombaX

+0

solo una actualización: Intenté ocupar la memoria (un bucle simple asignando algunas instancias de NSData), pero los datos centrales NUNCA liberaron la memoria (Intenté muchas veces, asignando 100MB de NSData, luego 200, luego 300 ... hasta que la aplicación se bloqueó). Parece que almacena en caché todos los datos de la búsqueda de fetch sin liberarlo, ¡NUNCA! Sé que puedo usar otros enfoques para alcanzar mi alcance, pero parece extraño. ¿Cuál es el significado de las propiedades de falla si continúan usando ram? – LombaX

Cuestiones relacionadas