2009-08-05 11 views
10

Sto lavorando a un'applicazione per iPhone che ottiene un numero di oggetti da un database. Mi piacerebbe archiviarli usando Core Data, ma ho problemi con le mie relazioni.Aggiunta di oggetti unici a Core Data

Un Dettaglio contiene un numero qualsiasi di POI (punti di interesse). Quando prelevo un set di POI dal server, contengono un ID di dettaglio. Per associare il POI al dettaglio (per ID), il mio processo è il seguente: Interrogare ManagedObjectContext per detailID. Se quel dettaglio esiste, aggiungete il poi a esso. In caso contrario, crea il dettaglio (ha altre proprietà che verranno popolate pigramente).

Il problema con questo è prestazioni. L'esecuzione di query costanti su Core Data è lenta, al punto che aggiungere un elenco di 150 POI richiede un minuto grazie alle molteplici relazioni coinvolte.

Nel mio vecchio modello, prima di Core Data (vari oggetti di cache NSDictionary) questo processo è stato super veloce (cercare una chiave in un dizionario, quindi crearlo se non esiste)

ho più rapporti rispetto a questo, ma praticamente ognuno deve fare questo controllo (alcuni sono molti per molti, e hanno un vero problema).

Qualcuno ha qualche suggerimento su come posso aiutare questo? Potrei eseguire meno query (cercando un numero di ID diversi), ma non sono sicuro di quanto questo possa essere d'aiuto.

Alcuni codice:

 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

Spiega i tuoi oggetti in modo più dettagliato e pubblica parte del codice che usi. È quasi del 100% probabile che ci sia un modo per fare ciò che stai già facendo molto più velocemente. – Sneakyness

risposta

3

Questa pagina fornisce un aiuto sulle prestazioni ottimizzando: http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW1

Anche se non molto efficiente, perché non li costruiscono in-memory con un NSDictionary? Leggi tutto da Core Data in un NSDictionary quindi unisci i tuoi dati, sostituendo tutto in Core Data.

+0

Idea interessante. Lo schema sarà quindi, ogni volta che si creano oggetti 1. Recupera il possibile intervallo di oggetti correlati, memorizza in NSDictionary con la chiave 2. Se l'ID esiste, aggiungi la relazione a quell'oggetto 3. Se l'ID non lo è, aggiungi il nuovo oggetto sia al dizionario che ai dati principali. Aggiungi la relazione. – Dimitri

4

Ho ottenuto tutto questo per funzionare davvero bene, grazie a Norman, che mi ha messo sulla strada giusta. Pubblicherò la mia classe di aiuto qui per gli altri.

Fondamentalmente, la mia classe helper cercherà se esiste un NSManagedObject per alcuni ID e può crearlo per alcuni ID. Questo si esegue abbastanza velocemente per me, con 1.000 operazioni di ricerca/creazione che richiedono circa 2 secondi sul mio iPhone (ho fatto anche alcune altre cose lì, la ricerca/creazione pura è probabilmente più veloce).

Fa questo memorizzando nella cache un dizionario di tutti gli NSManagedObjects e controllando la cache anziché eseguire una nuova NSFetchRequest.

Un paio di modifiche che potrebbero contribuire ad accelerare ancora di più: 1. selezionato solo le proprietà per le NSManagedObjects 2. ottenere solo la proprietà identificatore per il NSManagedObject in un dizionario, anziché l'intero oggetto. Nel mio test delle prestazioni, la singola query non era la parte lenta (ma con solo 1.000 elementi, mi aspetterei che fosse veloce). La parte lenta è stata la creazione degli oggetti.

#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

Inoltre, assicurati di spuntare l'opzione "indice" sulla tua proprietà identificatore. – Dimitri

+2

Grazie! Vuoi pubblicare anche il file .h? – ma11hew28

+0

Si prova a trovare l'entità con il valore dell'attributo controllando tutte le entità dall'archivio. Accedete a tutte queste entrate e cambia lo stato di errore in query reale. Penso che tu debba usare il predicato e la limitazione per NSFetchRequest ed eseguire la query. se conta> 0, significa che l'entità con attributo esiste già nell'archivio permanente. Dopo di ciò puoi accedere a questo risultato della richiesta per aggiornare la tua entità. –

6

controllare la sezione intitolata "Batch ha provocato l'errore" nella pagina intitolata "Core Data Performance" in di Xcode Core Data Guida alla programmazione che Norman legato alla sua risposta.

solo andare a prendere quei managedObjects i cui ID sono IN una collezione (NSSet, NSArray, NSDictionary) di ID degli oggetti restituiti dal server può essere ancora più efficace.

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

UPDATE: ho lavorato questo suggerimento in una soluzione per la acani usersView. Fondamentalmente, dopo aver scaricato uno JSON response of users, l'iPhone utilizza lo popular open source JSON framework per analizzare la risposta in uno NSArray di NSDictionary oggetti, ognuno dei quali rappresenta un utente. Quindi, crea un NSArray dei propri uid e recupera in batch i dati principali per vedere se ne esiste già uno sull'iPhone. In caso contrario, lo inserisce. In tal caso, aggiorna quelli esistenti solo se l'attributo updated è precedente a quello del server.