2015-08-16 17 views
8

Inserisco decine di migliaia di oggetti nella mia entità Core Data. Ho un singolo NSManagedObjectContext e sto chiamando save() sul contesto dell'oggetto gestito ogni volta che aggiungo un oggetto. Funziona ma mentre è in esecuzione, la memoria continua ad aumentare da circa 27M a 400M. E rimane a 400M anche dopo che l'importazione è finita.Perdita di memoria con grande batch di dati Core inserito in Swift

enter image description here

Ci sono una serie di domande su SO inserto in batch e tutti dicono a leggere Efficiently Importing Data, ma è in Objective-C e sto avendo problemi a trovare esempi reali a Swift che risolvono questo problema.

risposta

15

Ci sono alcune cose che si dovrebbe cambiare:

  • Creare un contesto NSPrivateQueueConcurrencyType oggetto gestito separato e fare i vostri inserti in modo asincrono in esso.
  • Non salvare dopo l'inserimento di ogni singolo oggetto entità. Inserisci i tuoi oggetti in lotti e poi salva ogni batch. Una dimensione del batch potrebbe essere qualcosa come 1000 oggetti.
  • Utilizzare autoreleasepool e reset per svuotare gli oggetti in memoria dopo ogni inserimento di batch e salvare.

Ecco come questo potrebbe funzionare:

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() 
    } 
} 

applicazione di questi principi mantenuto il mio utilizzo della memoria bassa e anche fatto inserire la massa più veloce.

enter image description here

Ulteriore lettura

  • efficiente Importazione di dati (vecchio link docs di Apple è rotto. Se si riesce a trovare, ti prego, aiutami aggiungerlo.)
  • Core Data Performance
  • Core Data (posto dell'Assemblea Generale)

Aggiornamento

La risposta di cui sopra è completamente riscritto. Grazie a @Mundi e @MartinR nei commenti per aver segnalato un errore nella mia risposta originale. E grazie a @JodyHagins nel this answer per avermi aiutato a capire e risolvere il problema.

+1

Sembra nel codice che si stia utilizzando lo stesso contesto dell'oggetto gestito, non uno nuovo. – Mundi

+0

Il contesto dell'oggetto gestito viene ricreato in ogni ciclo 'while' nel mio esempio sopra. Il ciclo 'while' rappresenta un batch di inserimenti, quindi un singolo batch utilizza lo stesso contesto dell'oggetto gestito, ma il batch successivo ne crea uno nuovo. Il mio problema in passato era che ho reso il contesto una proprietà di classe e non l'ho mai modificato. – Suragch

+1

@Suragch: Dipende dal modo in cui la proprietà 'managedObjectContext' è implementata nel delegato dell'applicazione, ma l'implementazione" normale "è una proprietà lazy che crea il contesto una volta per tutta la durata dell'app. In quel caso stai riutilizzando lo stesso contesto di Mundi. –