2012-09-06 18 views
9

Ho questo problema. Ho un database di immagini in Core Data. Prendo tutte le immagini (circa 80 MB) e inserisco un NSMutableArray. Gli oggetti vengono correttamente ineccepibili:Memoria dati di utilizzo e memoria Attenzione

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

Leggendo il registro, vedo che gli oggetti sono tutti correttamente ineccepibili Tuttavia, l'utilizzo di strumenti, vedo che 80 MB di memoria vengono utilizzati. Penso che questo sia il motivo per cui Core Data memorizza nella cache i suoi risultati e dovrebbe liberare la memoria quando è necessaria. Ma (e questo è il mio "problema"), se simulo un avviso di memoria, non succede niente! Gli 80 MB rimangono lì.

Guardando strumenti - allocazioni, il 80 MB sono utilizzati da molti Malloc: (esempio)

Grafico Categoria vivo Bytes # Living # transitorie In generale Byte # # Nel complesso Accantonamenti (netto/totale) 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 19 10,17 MB 62% 0.00, 0.00%

Questo è un link ad un'immagine di tutta la chiamata Tree: https://www.dropbox.com/s/du1b5a5wooif4w7/Call%20Tree.png

Qualche idea? Grazie

+0

Forse i dati di base liberano memoria su 'Memory Warning Level 2'? È possibile produrre un arresto anomalo della memoria con il tuo scenario? – brigadir

+0

Esiste un "metodo magico" per simulare un livello di avviso di memoria 2? O "semplicemente" devo consumare la memoria? – LombaX

+0

Non conosco alcun metodo di simulazione. Dovresti eseguire un'altra app "pesante" (ad esempio Appstore), mantenendo l'app in log della console di background e di tracciamento e nella scheda di memoria degli strumenti. L'avviso di 'livello 2' sarà menzionato in console - quindi dovresti guardare il grafico della memoria in quel momento. – brigadir

risposta

9

Ok, ho capito perché succede. Quando si effettua una richiesta di recupero per un'entità, anche se l'errore è abilitato, TUTTI i DATI di quell'entità vengono caricati in memoria. Compresi grandi dati binari. si può risolvere questo usando molti metodi:

1- impostazione questo sul vostro NSFetchRequest: [request setIncludesPropertyValues:NO]; impostazione NO, i dati non vengono caricati nella cache immediatamente, ma solo su richiesta (quando si accede alla proprietà e la colpa è sparato) Ma questo ha un "problema". Anche se si tenta di eseguire nuovamente il faulty (perché non è necessario immediatamente e si desidera liberare la memoria, utilizzando [self.managedObjectContext refreshObject:object mergeChanges:NO];), la memoria non viene liberata. La cache rimane attiva fino a quando viene eseguito il reset di managedObjectContext.

questo è meglio:

2- è possibile dividere i dati in entità separate. Nel mio caso avevo solo 2 proprietà: un url e dati di immagine. Ho diviso i dati in 2 entità con una relazione 1: 1: imagecache e imagedata. Creato un file fetchRequest per tutte le righe dell'entità "imagecache" (con la proprietà url) e, come nella precedente soluzione, non è stata memorizzata alcuna memoria. La corretta imagecache.relationship.image è stata correttamente risolta. L'accesso a questa proprietà ha provocato l'errore e il riempimento della cache. Ma in questo caso, fare [self.managedObjectContext refreshObject:object mergeChanges:NO]; sull'oggetto "imagecache" (l'oggetto "padre"), ha provocato la liberazione immediata della cache e della memoria, provocando nuovamente la proprietà imagecache.relationship.image. Attenzione: non fare sull'oggetto "figlio", se lo fai [self.managedObjectContext refreshObject:object.relationship mergeChanges:NO], per qualche motivo la cache non viene liberata. Penso che questo sia il motivo per cui attraversi la relazione.

3- Ho detto che si trattava principalmente di una questione accademica, la vera soluzione "tutto il giorno" (prestazioni migliori e meno mal di testa) per questi problemi è evitare di salvare i big data nel database dei dati di base. Puoi salvare i tuoi dati come file e memorizzare solo un riferimento (percorso file) oppure, con iOS 5 hai la possibilità di impostare "usa memoria esterna" su qualsiasi proprietà "Dati" all'interno del tuo modello di dati di base. Questo farà tutto il lavoro per te.

+1

Ehi, mi sto solo chiedendo se hai mai trovato altre soluzioni a questo problema. Sfortunatamente, non ho alcun grande oggetto di memoria NSData che possa usare la soluzione 3 per. Piuttosto, ho centinaia di migliaia di oggetti che vengono visualizzati come annotazioni sulla mappa. Raccolgo i lotti in modo da assicurarmi che se il dispositivo non ha abbastanza memoria posso limitare la quantità di dati visualizzati. Ma quando ricevo un avviso di memoria e chiamo refreshObject: mergeChanges: la memoria non è interessata come hai detto, e finisco per avere un crash di memoria. qualche idea? – horsejockey

0

Penso che dovresti caricare gli oggetti in memoria in batch.

la memoria rilasciata da coredata avviene dietro le quinte e non è necessario programmarlo; la cattiva notizia è che accade dietro le quinte e quindi può "magicamente" masticare memoria.

I percorsi attorno sono molti; ad esempio, utilizzare un predicato per selezionare solo le righe che devono assolutamente essere necessarie; non effettuare una chiamata generale per recuperare tutto e quindi scorrere l'elenco uno per uno. È più probabile che si verifichi un arresto anomalo quando si esegue la chiamata generale e CoreData tenta di caricare tutti gli oggetti.

+0

Sì, la prima soluzione che ho applicato è stata di recuperare solo i dati di cui avevo bisogno. Questa è principalmente una domanda "accademica". La documentazione è chiara su questo, la gestione della cache è fatta da Core Data e tutto avviene dietro le quinte, ma dice anche che in caso di memoria insufficiente, la memoria è libera. Mi aspettavo di vedere una diminuzione della memoria utilizzata dai dati principali dopo un avviso di memoria. Non vedendo questo, stavo pensando che c'era qualcosa di sbagliato nel mio codice ...! Mi piacerebbe vedere "con i miei occhi" Core Data liberando memoria in situazioni di memoria insufficiente :-) – LombaX

+0

solo un aggiornamento: ho provato ad occupare memoria (un semplice ciclo che alloca alcune istanze NSData), ma Core Data non ha liberato memoria (Ho provato un sacco di volte, allocando 100MB di NSData, poi 200, poi 300 ... fino a quando l'app si è bloccata). Sembra che memorizzi nella cache tutti i dati della fetchrequest senza liberarlo, MAI! So che posso usare altri approcci per raggiungere il mio scopo, ma sembra strano. Qual è il significato delle proprietà di guasto se continuano a utilizzare RAM? – LombaX