2015-08-16 9 views
8

Ich füge Zehntausende von Objekten in meine Core Data-Entität ein. Ich habe eine einzige NSManagedObjectContext und ich rufeauf dem verwalteten Objekt Kontext jedes Mal, wenn ich ein Objekt hinzufügen. Es funktioniert, aber während es läuft, steigt der Speicher von etwa 27 Millionen auf 400 Millionen. Und es bleibt bei 400M, auch wenn der Import beendet ist.Speicherleck mit großen Core Data Batch-Einfügung in Swift

enter image description here

Es gibt eine Reihe von SO Fragen über Batch-Einsatz und jedem sagt Efficiently Importing Data zu lesen, aber es ist in Objective-C und ich habe Probleme beim realen Beispielen in Swift zu finden, die dieses Problem lösen.

Antwort

15

Es gibt ein paar Dinge, die Sie ändern sollen:

  • Erstellen Ihnen einen separaten NSPrivateQueueConcurrencyType Objektkontext verwaltet und es Ihre Einsätze asynchron tun.
  • Speichern Sie nicht nach dem Einfügen jedes einzelnen Entitätsobjekts. Fügen Sie Ihre Objekte in Stapeln ein und speichern Sie dann jeden Stapel. Eine Stapelgröße kann etwa 1000 Objekten entsprechen.
  • Verwenden Sie autoreleasepool und reset, um die Objekte im Speicher nach jedem Einfügen und Speichern zu löschen.

Hier ist, wie dies funktionieren könnte:

let managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType) 
managedObjectContext.persistentStoreCoordinator = (UIApplication.sharedApplication().delegate as! AppDelegate).persistentStoreCoordinator // or wherever your coordinator is 

managedObjectContext.performBlock { // runs asynchronously 

    while(true) { // loop through each batch of inserts 

     autoreleasepool { 
      let array: Array<MyManagedObject>? = getNextBatchOfObjects() 
      if array == nil { break } 
      for item in array! { 
       let newEntityObject = NSEntityDescription.insertNewObjectForEntityForName("MyEntity", inManagedObjectContext: managedObjectContext) as! MyManagedObject 
       newObject.attribute1 = item.whatever 
       newObject.attribute2 = item.whoever 
       newObject.attribute3 = item.whenever 
      } 
     } 

     // only save once per batch insert 
     do { 
      try managedObjectContext.save() 
     } catch { 
      print(error) 
     } 

     managedObjectContext.reset() 
    } 
} 

Anwendung dieser Grundsätze hielt meine Speicherauslastung gering und machte auch die Masse schneller einsetzen.

enter image description here

Weiterführende Literatur

  • Effizientes Importieren von Daten (alte Apple-docs Verbindung unterbrochen wird. Wenn Sie es nicht finden, bitte helfen Sie mir etwas hinzufügen.)
  • Core Data Performance
  • Core Data (Hauptpost)

aktualisieren

Die obige Antwort komplett neu geschrieben wird. Danke an @Mundi und @MartinR in den Kommentaren für einen Hinweis auf einen Fehler in meiner ursprünglichen Antwort. Und danke an @JodyHagins in this answer für das Verständnis und die Lösung des Problems.

+1

Es scheint, dass Sie in Ihrem Code den gleichen Kontext verwalteter Objekte verwenden, nicht einen neuen. – Mundi

+0

Der Kontext des verwalteten Objekts wird in meinem obigen Beispiel in jeder while-Schleife neu erstellt. Die "while" -Schleife stellt einen Stapel von Einfügungen dar, sodass ein einzelner Stapel den gleichen Kontext für verwaltete Objekte verwendet, der nächste Stapel jedoch einen neuen erstellt. Mein Problem in der Vergangenheit war, dass ich den Kontext zu einer Klasseneigenschaft gemacht und nie geändert habe. – Suragch

+1

@Suragh: Das hängt davon ab, wie die Eigenschaft 'managedObjectContext' im Anwendungsdelegaten implementiert wird, aber die" normale "Implementierung ist eine faule Eigenschaft, die den Kontext einmal für die Lebensdauer der App erstellt. In diesem Fall verwenden Sie denselben Kontext wie Mundi sagte. –