¿Mejorar el process de duplicar la database del server a una database de clientes a través de JSON?

Tengo una aplicación henetworkingada de Apple (no AppStore) iOS para iPad que necesito refactorizar (fue escrita por otro desarrollador, mi pnetworkingecesor en mi trabajo actual).

Esta aplicación obtiene sus datos a través de JSON desde un server con database MSSQL. El esquema de la database tiene aproximadamente 30 tablas, las más amplias son: Cliente, Ciudad, Agencia cada una con unos 10.000 loggings cada una y se espera un mayor crecimiento en el futuro. Después de que se recibe el JSON (un par de JSON de request y respuesta para cada tabla), se asigna al CoreData, el process que también incluye pegar las entidades CoreData correspondientes (Cliente, Ciudad, Agencia y otros) entre sí estableciendo las relaciones entre estas entidades en la capa CoreData.

En sí mismo, el CoreData fetch-part (o la parte de lectura) del proyecto está muy optimizado; utiliza, supongo, casi todos los ajustes posibles de memory y performance CoreData tiene, es por eso que la capa de aplicación UI es muy rápida y receptiva, considere su trabajo como completamente satisfactorio y adecuado.


El problema es el process de preparación de la capa CoreData, es decir, el process de synchronization de server a cliente: lleva demasiado time. Considere 30 requestes de networking que den como resultado 30 packages JSON ("package" me refiero a "una tabla – una JSON"), que luego se asignan a 30 entidades CoreData, que luego se pegan juntas (las relaciones CoreData apropiadas se establecen entre ellas). Cuando vi por primera vez cómo se hace todo esto en este proyecto (demasiado lento), la primera idea que vino a mi mente fue:

"Por primera vez se realiza una synchronization completa (primer time de ejecución de la aplicación): se realiza una búsqueda de todos los datos de la base de datos en, por ejemplo, un file archivado (algo así como volcado de la database) y luego, de alguna manera, se importa en su totalidad a un Core Data tierra".

Pero entonces me di count de que, incluso si tal transmisión de tal volcado de un file era posible, CoreData todavía me exigiría realizar un enqueuedo de las entidades CoreData correspondientes para establecer las relaciones apropiadas entre ellos, de modo que es difícil imaginar que podría beneficio en el performance si dependiera de este esquema.

Además, mi colega me sugirió que considerara SQLite como una alternativa completa a Core Data, pero desafortunadamente no tengo experiencia en usarlo, es por eso que estoy completamente ciego para prever todas las consecuencias de una decisión de layout tan seria (incluso tener la el process de synchronization es muy lento, mi aplicación funciona , especialmente su performance de interfaz de usuario es muy bueno ahora). Lo único que puedo imaginar acerca de SQLite que, en contraste con Core Data, no me empujará a pegar algunas relaciones adicionales en un lado del cliente, porque SQLite tiene su buen sistema de keys externas antiguas, ¿no es así?


Y aquí están las preguntas (los encuestados, por favor, no mezclen estos puntos cuando responden, hay demasiada confusión acerca de todos ellos):

  1. ¿Alguien tiene esa experiencia de tomar el enfoque de "primera import de toda la database" por primera vez de la manera que he descrito anteriormente? Estaré muy agradecido de saber sobre cualquier solución si explotan el par JSON <-> CoreData o no.

  2. ¿Core Data tiene algún mecanismo de import global que puede permitir la creación masiva del esquema de 30 tablas correspondiente (posiblemente utilizando alguna fuente específica que no sea "30 packages de JSON" descrita anteriormente) sin la necesidad de configurar relaciones correspondientes para 30 entidades?

  3. ¿Hay alguna posibilidad de acelerar el process de synchronization si 2) es imposible? Aquí me refiero a las mejoras del esquema JSON <-> CoreData actual que usa mi aplicación.

  4. Migración a SQLite: ¿debería considerar dicha migration? ¿De qué me beneficiaré? ¿Cómo se vería todo el process de replicación-> transmisión-> preparaciones de clientes ?

  5. Otras alternativas a CoreData y SQLite: ¿cómo podrían ser o parecer?

  6. ¿Alguna otra idea o visión que pueda tener sobre la situación que he descrito?


ACTUALIZACIÓN 1

Aunque la respuesta escrita por Mundi es buena (una gran JSON, "No" para usar SQLite), todavía me interesa si hay otras ideas sobre el problema que he descrito.


ACTUALIZACIÓN 2

Traté de usar mi inglés ruso de la mejor manera posible para describir mi situación con la esperanza de que mi pregunta pudiera quedar bastante clara para todos los que la lean. Con esta segunda actualización trataré de proporcionarle más guías para que mi pregunta sea aún más clara.

Por favor, considere dos dicotomías:

  1. ¿Qué puedo / debo usar como capa de datos en el cliente iOS? ¿CoreData vs SQLite?
  2. ¿Qué puedo / debo usar como capa de transporte? – JSON (JSON único a la vez como se sugiere en la respuesta, incluso comprimido tal vez) o algunos vertederos de DB-sí (si es posible, por supuesto), aviso que soy también preguntando esto en mi pregunta).

Creo que es bastante obvio el "sector" que está formado por la intersección de estas dos dicotomías, eligiendo CoreData del primero y JSON del segundo es el pnetworkingeterminado más difundido en el mundo de desarrollo de iOS y también lo utiliza mi aplicación de esta pregunta

Dicho esto, afirmo que estaría agradecido de ver cualquier respuesta sobre el par CoreData-JSON, así como las respuestas considerando el uso de cualquier otro "sector" (¿qué pasa con optar por SQLite y algún tipo de enfoque de volcados, por qué no?)

Además, es importante tener en count que no deseo dejar la opción actual para algunas otras alternativas, solo quiero que la solución funcione rápidamente tanto en la fase de synchronization como en la interfaz de usuario de su uso. ¡Así que las respuestas sobre la mejora del esquema actual, así como las respuestas que sugieren que los otros esquemas son bienvenidos!

Ahora, vea la siguiente actualización # 3 que proporciona más detalles para mi situación actual de CoreData-JSON:


ACTUALIZACIÓN 3

Como ya he dicho, actualmente mi aplicación recibe 30 packages de JSON: un package para toda la table. Tomemos tablas amplias, por ejemplo: Cliente, Agencia, Ciudad.

Es Core Data, así que si un logging de client tiene un campo de agency_id no vacío, necesito crear una nueva entidad de Core Data de Agency (NSManagedObject subclass) de class Agency (NSManagedObject subclass) y llenarla con los datos de JSON de este logging, por eso necesito tener Core Entidad de datos para esta agencia de class Agency (NSManagedObject's subclass) , y finalmente necesito hacer algo como client.agency = agency; y luego llame a [currentManagedObjectContext save:&error] . Si lo hago de esta manera, más tarde puedo pedirle a este cliente que lo .agency y pedirle a su propiedad .agency que encuentre la entidad correspondiente. Espero estar completamente cuerdo cuando lo hago de esta manera.

Ahora imagina que este patrón se aplica a la siguiente situación:

Acabo de recibir los siguientes 3 packages JSON separados: 10000 clientes y 4000 ciudades y 6000 agencias (el cliente tiene una ciudad, la ciudad tiene muchos clientes, el cliente tiene agencia, la agencia tiene muchos clientes, la agencia tiene una ciudad, la ciudad tiene muchas agencias).

Ahora quiero configurar las siguientes relaciones en el nivel de datos básicos: Quiero que mi cliente de entidad client esté conectado a una ciudad correspondiente y a la agencia correspondiente.

La implementación actual de esto en el proyecto hace algo muy feo:

  1. Dado que el order de dependencia es el siguiente: Ciudad -> Agencia -> Cliente, es decir, la Ciudad debe hornearse primero, la aplicación comienza a crear entidades para la Ciudad y las persiste hasta Core Data.

  2. Luego se trata de JSON de agencias: itera a través de cada logging de JSON, para cada agencia, crea una nueva agency entidad y por su city_id , obtiene la city entidad correspondiente y la conecta usando agency.city = city . Después de la iteración a través de todo el set de las agencias JSON, se guarda el context del object gestionado actual (en realidad, el file [managedObjectContext save:] se realiza varias veces, cada uno después de 500 loggings procesados). En este paso, es obvio que search una de las 4000 ciudades para cada cliente para cada una de las 6000 agencias tiene un gran impacto en el performance en todo el process de synchronization.

  3. Entonces, finalmente se trata con JSON de clientes: al igual que en la etapa 2 anterior, itera a través de toda la matriz JSON de 10000 elementos y, uno por uno, realiza la búsqueda de agencias correspondientes y ciudades de ZOMG, y esto afecta el performance general en la misma como hace la etapa anterior 2.

Todo es muy malo.

La única optimization del performance que puedo ver aquí es que la primera etapa podría dejar un dictionary grande con ids de ciudades (me refiero a NSNumber de IDs reales) y las entidades de la ciudad falladas como valores) por lo que sería posible evitar el process de búsqueda feo de los siguientes etapa 2 y luego hacer lo mismo en el escenario 3 usando el truco de almacenamiento en caching análogo, pero el problema es que hay muchas más relaciones entre las 30 tablas que acaba de describir [Cliente-Ciudad, Cliente-Agencia, Agencia-Ciudad] el procedimiento final que involucre un almacenamiento en caching de todas las entidades será probablemente el que afecte los resources de las reservas de dispositivos iPad para mi aplicación.


ACTUALIZACIÓN 4

Mensaje para futuros encuestados: he hecho todo lo posible para que esta respuesta sea bien detallada y bien formada, y realmente espero que responda con respuestas detalladas . Sería genial si su respuesta realmente aborda la complejidad del problema que se analiza aquí y complementa mis esfuerzos que he realizado para que mi pregunta sea clara y general tanto como sea posible. Gracias.

ACTUALIZACIÓN 5

Temas relacionados: Datos principales en el cliente (iOS) para almacenar datos en caching desde una estrategia de server , intentando hacer una request POST con RestKit y mapear la respuesta a los datos principales .

ACTUALIZACIÓN 6

Incluso después de que ya no es posible abrir nuevas recompensas y hay una respuesta aceptada, todavía me complacería ver otras respuestas que contengan información adicional sobre el problema que aborda este tema. Gracias por adelantado.

Tengo experiencia en un proyecto muy similar. Las inserciones de Core Data toman un time, por lo que condicionamos al usuario que esto tomará un time, pero solo la primera vez . El mejor ajuste de performance fue, por supuesto, para get el tamaño de lote justo entre las guardadas, pero estoy seguro de que eres consciente de eso.

Una sugerencia de performance: he intentado algunas cosas y he descubierto que crear muchos hilos de descarga puede ser un éxito en el performance, supongo que porque para cada request hay cierta latencia del server, etc.

En cambio, descubrí que download todo el JSON de una vez fue mucho más rápido. No sé cuántos datos tienes, pero probé con> 100.000 loggings y una cadena JSON de 40MB + esto funciona muy rápido, por lo que el cuello de botella es solo las inserciones de Core Data. Con un pool @autorelease esto incluso se realizó de forma aceptable en un iPad de primera generación.

Manténgase alejado de la API SQLite: le llevará más de un año (proporcionó alta productividad) para replicar las optimizaciones de performance que obtiene de la caja con Core Data.

En primer lugar, está haciendo mucho trabajo, y llevará algún time, no importa cómo lo corte, pero hay maneras de mejorar las cosas.

Recomendaría hacer sus búsquedas en lotes, con un tamaño de lote que coincida con el tamaño de lote para procesar nuevos objects. Por ejemplo, al crear nuevos loggings de la Agency , haz algo como:

  1. Asegúrese de que el lote actual de la Agency esté orderado por city_id . (Explicaré por qué más tarde).

  2. Obtenga la identificación de la City para cada Agency en el lote. Dependiendo de cómo esté estructurado su JSON, esto probablemente sea una línea como esta (ya que valueForKey funciona en matrices):

     NSArray *cityIDs = [myAgencyBatch valueForKey:@"city_id"]; 
  3. Obtenga todas las instancias de City para el pase actual en una búsqueda utilizando las ID que encontró en el paso anterior. Ordena los resultados por city_id . Algo como:

     NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"City"]; NSPnetworkingicate *pnetworkingicate = [NSPnetworkingicate pnetworkingicateWithFormat:@"city_id in %@", cityIDs]; [request setPnetworkingicate:pnetworkingicate]; [request setSortDescriptors:@[ [NSSortDescriptor sortDescriptorWithKey:@"city_id" ascending:YES] ]]; NSArray *cities = [context executeFetchRequest:request error:nil]; 

Ahora , tiene una matriz de Agency y otra de City , ambas orderadas por city_id . city_id coincidencias para configurar las relaciones (marque city_id en caso de que las cosas no coincidan). Guarde los cambios y continúe con el siguiente lote.

Esto networkingucirá drásticamente el número de búsquedas que necesita hacer, lo que debería acelerar las cosas. Para get más información sobre esta técnica, consulte Implementación de Find-or-Create Efficiently en los documentos de Apple.

Otra cosa que puede ayudar es "calentar" la caching interna de Core Data con los objects que necesita antes de comenzar a searchlos. Esto ahorrará time más adelante porque get valores de propiedad no requerirá un viaje al almacén de datos. Para esto, haría algo como:

 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"City"]; // no pnetworkingicate, get everything [request setResultType:NSManagedObjectIDResultType]; NSArray *notUsed = [context executeFetchRequest:request error:nil]; 

… y luego olvídate de los resultados. Esto es superficialmente inútil, pero alterará el estado interno de los Datos básicos para un acceso más rápido a las instancias de la City más adelante.

Ahora en cuanto a sus otras preguntas,

  • Usar SQLite directamente en lugar de Core Data podría no ser una elección terrible para su situación. El beneficio sería que no tendría que configurar las relaciones, ya que podría usar campos como city_id como keys externas. Entonces, import rápida. La desventaja, por supuesto, es que tendrás que hacer tu propio trabajo para convertir los objects de tu model a / de los loggings SQL, y probablemente reescribir una gran cantidad de código existente que asume Core Data (por ejemplo, cada vez que sigues una relación que ahora necesita search loggings con esa key externa). Este cambio podría solucionar los problemas de performance de import, pero los efectos secundarios podrían ser significativos.

  • JSON es generalmente un muy buen formatting si está transmitiendo datos como text. Si pudiera preparar un almacén de Core Data en el server, y si utilizara ese file tal como está en lugar de tratar de fusionarlo en un almacén de datos existente, eso ciertamente aceleraría las cosas. Su process de import se ejecutaría una vez en el server y nunca más. Pero esos son grandes "si", especialmente el segundo. Si llega a donde necesita fusionar un nuevo almacén de datos del server con los datos existentes, vuelve enseguida a donde se encuentra ahora.

¿Tienes el control del server? Pregunto, porque suena como lo hace en el siguiente párrafo:

"Por primera vez se realiza una synchronization completa (la primera hora de inicio de la aplicación): realiza la búsqueda de toda la información de la database en, por ejemplo, un file archivado (algo así como volcado de la database) y luego de alguna manera lo importa en su totalidad a la base CoreData "

Si es posible enviar un volcado, ¿por qué no enviar el file Core Data en sí mismo? Core Data (por defecto) está respaldado por una database SQLite: ¿por qué no generar esa database en el server, cerrarla y enviarla a través del cable?

Esto significaría que podría eliminar todo el análisis de JSON, las requestes de networking, etc. y replacelo con una simple descarga de files y extracción de files. Lo hicimos en un proyecto y mejoró el performance de forma inconmensurable.

  1. Para cada fila de su tabla, debe haber una columna de date y hora . Si no hay uno, debe agregarlo.
  2. La primera vez que descarga el volcado de la database, almacena la última date y hora de la actualización.
  3. En cada próxima vez, le indica a la database que devuelva solo aquellos loggings que fueron modificados o actualizados desde la operación de descarga anterior. También debería haber una bandera "eliminada" para que elimines los loggings desaparecidos.
  4. Luego, solo necesita actualizar ciertos loggings coincidentes para ahorrar time en todos los frentes.

Para acelerar la synchronization por primera vez, también puede enviar una database de semillas con la aplicación, para que pueda importarse inmediatamente sin ninguna operación de networking.

  1. Descargue los files JSON a mano.
  2. Póngalos en su proyecto.
  3. En algún lugar de la configuration del proyecto o de los files del encabezado, tome nota de la date y hora de descarga.
  4. En la primera ejecución, ubique y cargue dichos files, luego proceda como si los estuviera actualizando.
  5. En caso de duda, consulte el manual.

Ejemplo:

 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"cities" ofType:@"json"]; NSData *citiesData = [NSData dataWithContentsOfFile:filePath]; // I assume that you're loading an array NSArray *citiesSeed = [NSJSONSerialization JSONObjectWithData:citiesData options:NSJSONReadingMutableContainers error:nil]; 

Aquí tienes mis recomendaciones:

  • Utiliza magicalrecord . Es un envoltorio CoreData que le ahorra muchos códigos repetitivos, además de características muy interesantes.
  • Descargue todo el JSON en una request, como otros sugirieron. Si puede insert el primer documento JSON en la aplicación, puede save el time de descarga y comenzar a llenar la database justo cuando abre la aplicación por primera vez. Además, con magicalrecord es bastante fácil realizar esta operación de save en un hilo por separado y luego sincronizar todos los contexts automáticamente. Esto puede mejorar la capacidad de respuesta de su aplicación.
  • Parece que deberías refactorizar ese método desagradable una vez que hayas resuelto el primer problema de import. Una vez más, sugeriría usar magicalrecord para crear fácilmente esas entidades.

Recientemente hemos movido un proyecto bastante grande desde Core Data a SQLite, y uno de los motivos principales fue el performance de inserción masiva. Hubo algunas características que perdimos en la transición, y no le aconsejaría que haga el cambio si puede evitarlo. Después de la transición a SQLite, en realidad teníamos problemas de performance en áreas distintas de las inserciones a granel, que Core Data manejaba de forma transparente para nosotros, y aunque reparamos esos nuevos problemas, se tardó bastante time en volver a funcionar. Aunque hemos invertido time y esfuerzo en la transición de Core Data a SQLite, no puedo decir que haya arrepentimientos.

Con eso aclarado, le sugeriría que obtenga algunas mediciones de línea de base antes de arreglar el performance del inserto masivo.

  1. Mida cuánto time lleva insert esos loggings en el estado actual.
  2. Omita configurar las relaciones entre esos objects por completo, y luego mida el performance de inserción.
  3. Cree una simple database SQLite y mida el performance de inserción con eso. Esto debería proporcionarle una muy buena estimación de reference de cuánto time lleva realizar las inserciones SQL reales y también le dará una buena idea de la sobrecarga de los Datos del Núcleo.

Algunas cosas que puedes probar para acelerar las inserciones:

  1. Asegúrese de que no hay controlleres de resultados capturados activos cuando esté realizando las inserciones masivas. Por activo, me refiero a los controlleres de resultados obtenidos que tienen un delegado no nulo. En mi experiencia, el seguimiento de cambios de Core Data fue la operación más cara al tratar de realizar inserciones masivas.
  2. Realice todos los cambios en un solo context y deje de combinar cambios desde diferentes contexts hasta que se completen las inserciones en bloque.

Para get más información sobre lo que realmente está sucediendo bajo el capó, habilite la debugging de Core Data SQL y vea las consultas SQL que se están ejecutando. Idealmente, querrías ver muchos INSERT, y algunas ACTUALIZACIONES. Pero si encuentra demasiados SELECT y / o ACTUALIZACIONES, entonces es una señal de que está haciendo demasiada lectura o actualización de objects.

Utilice el instrumento de perfilador de Core-Data para get una mejor descripción de alto nivel de lo que está sucediendo con Core Data.

He decidido escribir mi propia respuesta resumiendo las técnicas y consejos que encontré útiles para mi situación. Gracias a todas las personas que publicaron sus respuestas.


I. Transporte

  1. "Un JSON". Esta es la idea que quiero probar. Gracias @mundi

  2. La idea de archivar JSON antes de enviarlo a un cliente, ya sea un package JSON o 30 'una tabla – un package' por separado.


II. Configurar relaciones de datos centrales

Describiré un process de import de JSON-> CoreData importando una operación imaginaria de import grande como si se hubiera realizado en un método (no estoy seguro de si será así o no, tal vez lo dividí en trozos lógicos).

Imaginemos que en mi aplicación imaginaria hay 15 tables amplias, donde "capacitadas" significa "no se pueden save en la memory a la vez, deben importarse usando lotes" y 15 tablas no capacitivas cada una con <500 loggings, por ejemplo:

Espacioso:

  • ciudades (15k +)
  • clientes (30k +)
  • usuarios (15k +)
  • events (5k +)
  • acciones (2k +) …

Pequeña:

  • tipo_cliente (20-)
  • visit_types (10-)
  • posiciones (10-) …

Imaginemos que ya tengo los packages JSON descargados y analizados en variables compuestas NSArray / NSDictionary: tengo citiesJSON, clientsJSON, usersJSON, …

1. Trabaja primero con tablas pequeñas.

Mi pseudo-método comienza con la import de pequeñas tablas primero. Tomemos la tabla client_types: iterar a través de clientTypesJSON y crear objects ClientType (subclasss de NSManagedObject). Más que eso colecciono objects resultantes en un dictionary con estos objects como sus valores y "ids" (foreign keys) de estos objects como keys.

Aquí está el pseudocódigo:

 NSMutableDictionary *clientTypesIdsAndClientTypes = [NSMutableDictionary dictionary]; for (NSDictionary *clientTypeJSON in clientsJSON) { ClientType *clientType = [NSEntityDescription insertNewObjectForEntityForName:@"ClientType" inManagedObjectContext:managedObjectContext]; // fill the properties of clientType from clientTypeJSON // Write prepanetworking clientType to a cache [clientTypesIdsAndClientTypes setValue:clientType forKey:clientType.id]; } // Persist all clientTypes to a store. NSArray *clientTypes = [clientTypesIdsAndClientTypes allValues]; [managedObjectContext obtainPermanentIDsForObjects:clientTypes error:...]; // Un-fault (unload from RAM) all the records in the cache - because we don't need them in memory anymore. for (ClientType *clientType in clientTypes) { [managedObjectContext refreshObject:clientType mergeChanges:NO]; } 

El resultado es que tenemos un montón de dictionarys de pequeñas tablas, cada uno con el set correspondiente de objects y sus identificadores. Los usaremos más tarde sin una nueva búsqueda porque son pequeños y sus valores (NSManagedObjects) ahora son fallas.

2. Utilice el dictionary de caching de objects de tablas pequeñas obtenidas durante el paso 1 para establecer relaciones con ellos

Consideremos los clients tablas complejas: tenemos clientsJSON y tenemos que configurar una relación clientType para cada logging de cliente, es fácil porque tenemos un caching con clientTypes y sus ids:

 for (NSDictionary *clientJSON in clientsJSON) { Client *client = [NSEntityDescription insertNewObjectForEntityForName:@"Client" inManagedObjectContext:managedObjectContext]; // Setting up SQLite field client.client_type_id = clientJSON[@"client_type_id"]; // Setting up Core Data relationship beetween client and clientType client.clientType = clientTypesIdsAndClientTypes[client.client_type_id]; } // Save and persist 

3. Lidiando con tablas grandes – lotes

Consideremos a un gran clientsJSON con 30k + clientes en él. No repetimos a través de todos los clientsJSON sino que lo dividimos en trozos de tamaño apropiado (500 loggings), de modo que [managedObjectContext save:...] se llama cada 500 loggings. También es importante ajustar la operación con cada lote de 500 loggings en un @autoreleasepool block – consulte Reducción de la sobrecarga de memory en la Guía de performance de Core Data

Tenga cuidado: el paso 4 describe la operación aplicada a un lote de 500 loggings, no a un clientsJSON integer clientsJSON ¡ clientsJSON !

4. Lidiando con tablas grandes – estableciendo relaciones con tablas grandes

Considere el siguiente método, lo usaremos en un momento:

 @implementation NSManagedObject (Extensions) + (NSDictionary *)dictionaryOfExistingObjectsByIds:(NSArray *)objectIds inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext { NSDictionary *dictionaryOfObjects; NSArray *sortedObjectIds = [objectIds sortedArrayUsingSelector:@selector(compare:)]; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass(self)]; fetchRequest.pnetworkingicate = [NSPnetworkingicate pnetworkingicateWithFormat:@"(id IN %@)", sortedObjectIds]; fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey: @"id" ascending:YES]]; fetchRequest.includesPropertyValues = NO; fetchRequest.returnsObjectsAsFaults = YES; NSError *error; NSArray *fetchResult = [managedObjectContext executeFetchRequest:fetchRequest error:&error]; dictionaryOfObjects = [NSMutableDictionary dictionaryWithObjects:fetchResult forKeys:sortedObjectIds]; return dictionaryOfObjects; } @end 

Consideremos el package clientsJSON contiene un lote (500) de loggings de Client que necesitamos save. También necesitamos establecer una relación entre estos clientes y sus agencias ( Agency , key externa es agency_id ).

 NSMutableArray *agenciesIds = [NSMutableArray array]; NSMutableArray *clients = [NSMutableArray array]; for (NSDictionary *clientJSON in clientsJSON) { Client *client = [NSEntityDescription insertNewObjectForEntityForName:@"Client" inManagedObjectContext:managedObjectContext]; // fill client fields... // Also collect agencies ids if ([agenciesIds containsObject:client.agency_id] == NO) { [agenciesIds addObject:client.agency_id]; } [clients addObject:client]; } NSDictionary *agenciesIdsAndAgenciesObjects = [Agency dictionaryOfExistingObjectsByIds:agenciesIds]; // Setting up Core Data relationship beetween Client and Agency for (Client *client in clients) { client.agency = agenciesIdsAndAgenciesObjects[client.agency_id]; } // Persist all Clients to a store. [managedObjectContext obtainPermanentIDsForObjects:clients error:...]; // Un-fault all the records in the cache - because we don't need them in memory anymore. for (Client *client in clients) { [managedObjectContext refreshObject:client mergeChanges:NO]; } 

La mayor parte de lo que utilizo aquí se describe en estas guías de Apple: Rendimiento de Core Data , Importación eficiente de datos . Entonces, el resumen de los pasos 1-4 es el siguiente:

  1. Convierta los objects en fallas cuando se conserven, por lo que sus valores de propiedad se vuelven innecesarios a medida que la operación de import va más allá.

  2. Construya dictionarys con objects como valores y sus ids como keys, de modo que estos dictionarys puedan servir como tablas de búsqueda cuando construyan una relación entre estos objects y otros objects.

  3. Use @autoreleasepool cuando se repite a través de una gran cantidad de loggings.

  4. Use un método similar a dictionaryOfExistingObjectsByIds oa un método que Tom haga reference en su respuesta, desde la import eficiente de datos : un método que tiene un pnetworkingicado de SQL IN detrás para networkingucir significativamente una serie de búsquedas. Lea la respuesta de Tom y haga reference a la guía correspondiente de Apple para comprender mejor esta técnica.


Buena lectura sobre este tema

objc.io número 4: import de grandes sets de datos