2012-09-06 14 views
9

Ich habe dieses Problem. Ich habe eine Datenbank von Bildern in Core Data. Ich hole alle Bilder (ca. 80 MB) und gebe ein NSMutableArray ein. Die Objekte richtig bemängelt werden:Core Data Speicherverbrauch und Speicher Warnung

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

das Protokoll lesen, ich sehe, dass die Objekte sind alle richtig jedoch bemängelt, Instruments, ich sehe, dass 80 MB Speicher verwendet werden. Ich denke, das ist der Grund, warum Core Data seine Ergebnisse zwischenspeichert und den Speicher freigeben sollte, wenn er benötigt wird. Aber (und das ist mein "Problem"), wenn ich eine Speicherwarnung simuliere, passiert nichts! Die 80MB bleiben dort.

bei Instrumenten der Suche - Zuweisungen werden die 80MB von vielen Malloc verwendet: (Beispiel)

Graph Kategorie Live-Bytes # Leben # Transitory Insgesamt Bytes # Insgesamt # Verrechnungen (Netto/Overall) 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%

Dies ist ein Link zu einem Bild des gesamten Aufrufbaum: https://www.dropbox.com/s/du1b5a5wooif4w7/Call%20Tree.png

Irgendwelche Ideen? Danke

+0

Vielleicht gibt Core Data Speicher auf 'Memory Warning Level 2' frei? Ist es möglich, mit Ihrem Szenario einen geringen Speicherausfall zu verursachen? – brigadir

+0

Gibt es eine "magische Methode", um eine Speicherwarnstufe 2 zu simulieren? Oder "einfach" muss ich Speicher verbrauchen? – LombaX

+0

Ich kenne keine Simulationsmethode. Sie sollten eine weitere "schwere" App (z. B. Appstore) ausführen und Ihre App im Hintergrund behalten sowie das Protokoll der Protokollkonsole und das Speicherdiagramm der Geräte verfolgen. Die 'level 2' Warnung wird in der Konsole erwähnt - also sollten Sie sich die Speichergrafik in diesem Moment ansehen. – brigadir

Antwort

9

Ok, ich habe verstanden, warum es passiert. Wenn Sie eine Abrufanforderung für eine Entität ausführen, werden ALLE DATEN dieser Entität in den Arbeitsspeicher geladen, auch wenn die Störung aktiviert ist. Einschließlich großer Binärdaten. Sie können dies mit vielen Methoden lösen:

1- diese Einstellung auf Ihrem NSFetchRequest: [request setIncludesPropertyValues:NO]; NO eingestellt hat, werden die Daten nicht in den Cache geladen sofort, sondern nur auf Anfrage (wenn Sie die Eigenschaft zugreifen, und der Fehler ist gefeuert) Aber das hat ein "Problem". Selbst wenn Sie versuchen, die Eigenschaft erneut zu melden (weil Sie es nicht sofort benötigen und den Speicher freigeben möchten, indem Sie [self.managedObjectContext refreshObject:object mergeChanges:NO]; verwenden) wird der Speicher nicht freigegeben. Der Cache bleibt so lange aktiv, bis der managedObjectContext zurückgesetzt wird.

Das ist besser:

2- Sie Ihre Daten in separate Einheiten aufteilen. In meinem Fall hatte ich nur 2 Eigenschaften: eine URL und Bilddaten. Ich habe die Daten in 2 Entitäten mit einer 1: 1 Beziehung aufgeteilt: imagecache und imagedata. Eine fetchRequest für die gesamte Zeile der "imagecache" -Entität (mit der URL-Eigenschaft) erstellt, und wie bei der vorherigen Lösung wurde kein Speicher zwischengespeichert. Das property imagecache.relationship.image wurde korrekt angezeigt. Wenn Sie auf diese Eigenschaft zugreifen, wird der Fehler ausgelöst und der Cache wird gefüllt. Aber in diesem Fall, [self.managedObjectContext refreshObject:object mergeChanges:NO]; auf dem "imagecache" -Objekt (das "Vater" -Objekt) zu tun, führte zum sofortigen Freigeben des Caches und des Speichers, Fehler erneut imagecache.relationship.image -Eigenschaft. Achtung: Tun Sie nicht auf das "Kind" -Objekt, wenn Sie [self.managedObjectContext refreshObject:object.relationship mergeChanges:NO] tun, aus irgendeinem Grund wird der Cache nicht freigegeben. Ich denke, das ist der Grund, warum du die Beziehung durchquerst.

3- Ich sagte, dies war vor allem eine akademische Frage, die echte "den ganzen Tag" -Lösung (bessere Leistung und weniger Kopfschmerzen) für diese Probleme ist es zu vermeiden, große Daten in Kerndatenbank zu speichern. Sie können Ihre Daten als Dateien speichern und nur einen Verweis (Dateipfad) speichern oder mit iOS 5 haben Sie die Möglichkeit, für jede "Daten" -Eigenschaft in Ihrem Kerndatenmodell "externen Speicher" zu verwenden. Dies wird die ganze Arbeit für Sie erledigen.

+1

Hey, ich frage mich nur, ob Sie jemals andere Lösungen für dieses Problem gefunden haben. Leider habe ich keine großen NSData-Speicherobjekte, für die ich Lösung 3 verwenden kann. Stattdessen habe ich Hunderttausende von Objekten, die als Anmerkungen auf der Karte angezeigt werden. Ich lade in Stapeln, damit ich sicherstellen kann, dass wenn das Gerät nicht genug Speicher hat, ich begrenzen kann, wie viel Daten angezeigt werden. Aber wenn ich eine Speicherwarnung bekomme und refreshObject anrufe: mergeChanges: Der Speicher ist nicht wie erwähnt betroffen, und es kommt zu einem Speicherabsturz. Irgendwelche Gedanken? – horsejockey

0

Ich denke, dass Sie die weniger Objekte im Stapelspeicher laden sollten.

Speicher von Coredata freigegeben passiert hinter den Kulissen und Sie müssen nicht für es programmieren; Die schlechte Nachricht ist, dass es hinter den Kulissen passiert und somit die Erinnerungen "magisch" auffressen kann.

Möglichkeiten um es sind viele; Verwenden Sie beispielsweise ein Prädikat, um nur die Zeilen auszuwählen, die Sie unbedingt benötigen. Rufen Sie nicht allgemein an, um alles abzurufen, und gehen Sie dann nacheinander durch die Liste. Mehr als wahrscheinlich werden Sie abstürzen, wenn Sie den allgemeinen Aufruf durchführen und CoreData versucht, alle Objekte zu laden.

+0

Ja, die erste Lösung, die ich anwendete, war, nur die Daten zu holen, die ich brauchte. Dies ist hauptsächlich eine "akademische" Frage. Die Dokumentation ist klar darüber, die Cache-Verwaltung erfolgt durch Core Data und alles geschieht hinter der Szene, aber es sagt sogar, dass im Falle von wenig Speicher der Speicher frei ist. Ich habe erwartet, dass nach einer Speicherwarnung die Speicherkapazität der Kerndaten abnimmt. Als ich das nicht sah, dachte ich, dass etwas in meinem Code nicht stimmt ...! Ich würde gerne sehen "mit meinen Augen" Core Data Speicher in Low-Memory-Situation zu befreien :-) – LombaX

+0

nur ein Update: Ich habe versucht, Speicher (eine einfache Schleife Zuweisung einige NSData Instanzen) zu belegen, aber Core-Daten nie Speicher freigegeben (Ich habe viele Male versucht, 100 MB NSData, dann 200, dann 300 ... zuzuteilen, bis die App abstürzte). Es scheint, dass es alle Daten des Abrufantrags zwischenspeichert, ohne es zu befreien, NIEMALS! Ich weiß, dass ich andere Ansätze verwenden kann, um mein Ziel zu erreichen, aber es scheint seltsam. Was bedeuten die fehlerhaften Eigenschaften, wenn sie weiterhin ram verwenden? – LombaX