2009-08-05 12 views
10

Estoy trabajando en una aplicación de iPhone que obtiene una cantidad de objetos de una base de datos. Me gustaría almacenar estos usando Core Data, pero estoy teniendo problemas con mis relaciones.Agregar objetos únicos a Core Data

Un detalle contiene cualquier cantidad de PDI (puntos de interés). Cuando recojo un conjunto de PDI del servidor, contienen una identificación detallada. Para asociar el PDI con el Detalle (por ID), mi proceso es el siguiente: Consulte el ManagedObjectContext para el detailID. Si ese detalle existe, agregue el poi a él. Si no es así, cree el detalle (tiene otras propiedades que se poblarán perezosamente).

El problema con esto es el rendimiento. Realizar consultas constantes a Core Data es lento, hasta el punto de que agregar una lista de 150 POI lleva un minuto gracias a las múltiples relaciones involucradas.

En mi viejo modelo, antes de Datos Básicos (varios objetos de caché NSDictionary) este proceso fue muy rápida (mirar hacia arriba una clave en un diccionario, entonces créelo si no existe)

tengo más relaciones que solo este, pero casi todos tienen que hacer este control (algunos son muchos para muchos y tienen un problema real).

¿Alguien tiene alguna sugerencia sobre cómo puedo ayudarlo? Podría realizar menos consultas (buscando una cantidad de ID diferentes), pero no estoy seguro de cuánto ayudará esto.

Algunos código:

 POI *poi = [NSEntityDescription 
       insertNewObjectForEntityForName:@"POI" 
       inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]]; 

    poi.POIid = [attributeDict objectForKey:kAttributeID]; 
    poi.detailId = [attributeDict objectForKey:kAttributeDetailID]; 
    Detail *detail = [self findDetailForID:poi.POIid]; 
    if(detail == nil) 
    { 
     detail = [NSEntityDescription 
        insertNewObjectForEntityForName:@"Detail" 
        inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]]; 
     detail.title = poi.POIid; 
     detail.subtitle = @""; 
     detail.detailType = [attributeDict objectForKey:kAttributeType]; 
    } 





-(Detail*)findDetailForID:(NSString*)detailID { 
NSManagedObjectContext *moc = [[UIApplication sharedApplication].delegate managedObjectContext]; 
NSEntityDescription *entityDescription = [NSEntityDescription 
              entityForName:@"Detail" inManagedObjectContext:moc]; 
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; 
[request setEntity:entityDescription]; 

NSPredicate *predicate = [NSPredicate predicateWithFormat: 
          @"detailid == %@", detailID]; 
[request setPredicate:predicate]; 
NSLog(@"%@", [predicate description]); 

NSError *error; 
NSArray *array = [moc executeFetchRequest:request error:&error]; 
if (array == nil || [array count] != 1) 
{ 
     // Deal with error... 
    return nil; 
} 
return [array objectAtIndex:0]; 
} 
+1

Explique sus objetos con más detalle y publique parte del código que utiliza. Es casi 100% probable que haya una manera de hacer lo que ya está haciendo mucho más rápido. – Sneakyness

Respuesta

3

Esta página ofrece un poco de ayuda en la optimización del rendimiento: http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW1

Aunque no es muy eficiente, ¿por qué no construirlos en memoria con un NSDictionary? Lea todo desde Core Data en un NSDictionary luego fusione sus datos, reemplazando todo en Core Data.

+0

Interesante idea. El patrón sería entonces, al crear objetos 1. Obtener el rango posible de objetos relacionados, almacenar en NSDictionary con la clave 2. Si la identificación existe, agregue la relación con ese objeto 3. Si la identificación no, agregue el nuevo objeto tanto para el diccionario como para Core Data. Agrega la relación. – Dimitri

4

Todo esto ha funcionado muy bien, gracias a Norman, que me puso en el camino correcto. Voy a publicar mi clase de ayuda aquí para otros.

Básicamente, mi clase auxiliar buscará si existe un objeto NSManagedObject para algún ID, y puede crearlo para algún ID. Esto se ejecuta lo suficientemente rápido para mí, con 1.000 operaciones de búsqueda/creación que toman alrededor de 2 segundos en mi iPhone (también hice algunas otras cosas allí, el descubrimiento/creación puro es probablemente más rápido).

Hace esto almacenando en caché un diccionario de todos los objetos NSManagedObjects, y comprobando ese caché en lugar de ejecutar un nuevo NSFetchRequest.

Un par de modificaciones que podrían ayudar a acelerar las cosas aún más lejos: 1. Obtener sólo determinada por las propiedades NSManagedObjects 2. Sólo obtener la propiedad identificador para el NSManagedObject en un diccionario, en lugar de todo el objeto. En mi prueba de rendimiento, la consulta individual no era la parte lenta (pero con solo 1.000 elementos, esperaba que fuera rápida). La parte lenta fue la creación de los artículos.

#import "CoreDataUniquer.h" 


@implementation CoreDataUniquer 

    //the identifying property is the field on the NSManagedObject that will be used to look up our custom identifier 
-(id)initWithEntityName:(NSString*)newEntityName andIdentifyingProperty:(NSString*)newIdProp 
{ 
    self = [super init]; 
    if (self != nil) { 
     entityName = [newEntityName retain]; 
     identifyingProperty = [newIdProp retain]; 
    } 
    return self; 
} 

-(NSManagedObject*)findObjectForID:(NSString*)identifier 
{ 
    if(identifier == nil) 
    { 
     return nil; 
    } 
    if(!objectList) 
    { 
     NSManagedObjectContext *moc = [(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]; 
     NSEntityDescription *entityDescription = [NSEntityDescription 
                entityForName:entityName inManagedObjectContext:moc]; 
     NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; 
     [request setEntity:entityDescription]; 

     NSError *error; 
     NSArray *array = [moc executeFetchRequest:request error:&error]; 
     objectList = [[NSMutableDictionary dictionary] retain]; 
     for (NSManagedObject* p in array) { 
      NSString* itemId = [p valueForKey:identifyingProperty]; 
      [objectList setObject:p forKey:itemId]; 
     } 
    } 
    NSManagedObject* returnedObject = [objectList objectForKey:identifier]; 
    return returnedObject; 
} 
-(NSManagedObject*)createObjectForID:(NSString*)identifier 
{ 

    NSManagedObject* returnedObject = [NSEntityDescription 
             insertNewObjectForEntityForName:entityName 
             inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]]; 
    [returnedObject setValue:identifier forKey:identifyingProperty]; 
    [objectList setObject:returnedObject forKey:identifier]; 
    return returnedObject; 
} 


- (void) dealloc 
{ 
    DESTROY(entityName); 
    DESTROY(identifyingProperty); 
    [super dealloc]; 
} 

@end 
+0

Además, asegúrese de marcar la opción "indexar" en su propiedad de identificador. – Dimitri

+2

¡Gracias! ¿Podría publicar también el archivo .h? – ma11hew28

+0

Intenta encontrar la entidad con el valor del atributo al marcar todas las entidades de la tienda. Usted accede a todas estas entidades y también cambia el estado de la falla a una consulta real. Creo que debe usar el predicado y la limitación para NSFetchRequest y ejecutar la consulta. si count> 0, significa que la entidad con atributo ya existe en la tienda persistente. Después de eso, puede acceder a este resultado de solicitud para actualizar su entidad. –

6

Mira la sección titulada "lotes con errores" en la página titulada "Núcleo de datos de rendimiento" en Guía de programación de Datos Básicos de Xcode que Norman vincula en su respuesta.

ir a buscar sólo aquellos cuyas ID managedObjects son IN una colección (NSSet, NSArray, NSDictionary) de identificadores de los objetos devueltos por el servidor puede ser aún más eficiente.

NSSet *oids = [[NSSet alloc] initWithObjects:@"oid1", @"oid2", ..., nil]; 
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"oid IN %@", oids]; 
[oids release]; 

ACTUALIZACIÓN: He trabajado este consejo en una solución para el acani usersView. Básicamente, después de descargar un JSON response of users, el iPhone usa el popular open source JSON framework para analizar la respuesta en un NSArray de NSDictionary objetos, cada uno representando un usuario. Luego, hace un NSArray de sus uids y realiza una búsqueda por lotes en Core Data para ver si alguno de ellos ya existe en el iPhone. Si no, lo inserta. Si es así, actualiza los que existen solo si su atributo updated es anterior al del servidor.