modèle de données de base: comment mettre à jour efficacement des informations locales avec les changements de réseau?

StackOverflow https://stackoverflow.com/questions/2209228

Question

J'ai une certaine inefficacité dans mon application que je voudrais comprendre et corriger.

Mon algorithme est:

fetch object collection from network
for each object:
  if (corresponding locally stored object not found): -- A
    create object
    if (a nested related object locally not found): -- B
      create a related object

Je fais le contrôle sur les lignes A et B en créant une requête sous-jacente avec la clé de l'objet en question qui fait partie de mon schéma. Je vois que A (toujours) et B (si l'exécution ramifié en partie) génèrent un SQL SELECT comme:

2010-02-05 01:57:51.092 app[393:207] CoreData: sql: SELECT <a bunch of fields> FROM ZTABLE1 t0 WHERE  t0.ZID = ? 
2010-02-05 01:57:51.097 app[393:207] CoreData: annotation: sql connection fetch time: 0.0046s
2010-02-05 01:57:51.100 app[393:207] CoreData: annotation: total fetch execution time: 0.0074s for 0 rows.
2010-02-05 01:57:51.125 app[393:207] CoreData: sql: SELECT <a bunch of fields> FROM ZTABLE2 t0 WHERE  t0.ZID = ? 
2010-02-05 01:57:51.129 app[393:207] CoreData: annotation: sql connection fetch time: 0.0040s
2010-02-05 01:57:51.132 app[393:207] CoreData: annotation: total fetch execution time: 0.0071s for 0 rows.

0.0071s pour une requête est bien sur un appareil 3GS, mais si vous ajoutez 100 de ces up, vous venez de recevoir un bloqueur 700ms.

Dans mon code, j'utilise une aide pour faire ces récupérations:

- (MyObject *) myObjectById:(NSNumber *)myObjectId {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:[self objectEntity]]; // my entity cache    
    [fetchRequest setPredicate:[self objectPredicateById:objectId]]; // predicate cache    
    NSError *error = nil;
    NSArray *fetchedObjects = [moc executeFetchRequest:fetchRequest error:&error];
    if ([fetchedObjects count] == 1) {
        [fetchRequest release];
        return [fetchedObjects objectAtIndex:0];
    }
    [fetchRequest release];
    return nil;
}

MyObject *obj = [self myObjectById];
if (!obj) {
   // [NSEntityDescription insertNewObjectForEntityForName: ... etc
}

Je sens cela est faux et je dois faire le chèque d'une autre façon. Il ne devrait frapper la base de données une fois et devrait provenir de la mémoire par la suite, non? (Le SQL est exécuté même pour les objets que je sais pour exister que localement et auraient dû être chargés dans la mémoire des requêtes précédentes.) Mais, si je ne l'ai myObjectId d'une source extérieure, c'est le meilleur que je pouvais penser.

Alors, peut-être la question est la suivante: si je myObjectId (une propriété Data Core int64 sur MyObject), comment dois-je vérifier correctement si l'objet local concerné existe dans le magasin de CD ou non? Précharger l'ensemble des correspondances possibles puis prédicat un tableau local?

(Une solution possible est en mouvement à un fil de fond Ce serait bien, sauf que quand je reçois les changements du fil et faire [moc mergeChangesFromContextDidSaveNotification: aNotification];. (Se objets changé de fil de fond par voie de notification ), bloque encore.)

Était-ce utile?

La solution

Lire "Mise en œuvre Find-ou Créer Efficacement" dans le Guide de programmation de base de données.

Fondamentalement, vous devez créer un tableau d'ID ou des propriétés comme des noms, ou tout ce que vous avez de l'entité objet géré.

Ensuite, vous devez créer un prédicat qui filtre les objets gérés en utilisant ce tableau.

[fetchRequest setPredicate:[NSPredicate predicateWithFormat: @"(objectID IN %@)", objectIDs]];

Bien sûr « objectIds » pourrait être quelque chose que vous pouvez utiliser pour identifier. Il ne doit pas être le NSManagedObjectID.

Ensuite, vous pouvez faire une demande d'extraction, et itérer les objets récupérés résultant, pour trouver des doublons. Ajouter un nouveau si elle n'existe pas.

Autres conseils

Vous pourriez probablement prendre une leçon de clients de messagerie.

Ils travaillent en interrogeant d'abord le serveur pour une liste d'ID de message. Une fois que le client a cette liste, il compare ensuite contre son magasin de données local pour voir si quelque chose est différent.

S'il y a une différence qu'il faut une des deux actions. 1. Si elle existe sur le client, mais pas le serveur et nous IMAP, puis supprimez localement. 2. Si elle existe sur le serveur, mais pas client, puis télécharger le reste du message.

Dans votre cas, la première requête pour tous les ids. Envoyez ensuite un suivi requête pour saisir toutes les données pour ceux que vous ne possédez pas déjà.

Si vous avez une situation où le dossier peut exister localement, mais peut-être été mis à jour depuis le dernier téléchargement sur le serveur, votre requête devrait inclure la dernière date mise à jour.

Vous devriez faire un travers d'extraction de tous les objets, mais seulement chercher l'ID du serveur pour les objets.

Utiliser setPropertiesToFetch: avec setResultType:. réglé sur NSDictionaryResultType

Il ressemble à ce que vous avez besoin est un NSSet de NSManagedObjectIDs qui est chargé dans la mémoire ou stockées quelque part plus rapidement accessible que votre magasin d'objets persistants.

De cette façon, vous pouvez comparer ID d'objet à partir du réseau avec ID d'objet à partir de votre cache sans avoir à effectuer une demande d'extraction sur un grand nombre de données.

Peut-être ajouter l'ID dans le cache à l'intérieur -awakeFromInsert au sein de vos classes d'entités gérées?

Après avoir combattu avec ce même problème pour les âges, je me suis finalement trébuché à travers cette entrée de blog qui a totalement résolu (et est un bloc de code réutilisable en prime!).

http://henrik.nyh.se/2007/01/importing-legacy-data-into-core-data-with-the-find-or-create-or-delete-pattern

Alors que l'exemple de code ne couvre pas la partie du réseau; il vous suffit de le charger dans un NSDictionary. puis cette traite de la synchronisation du contexte local de base de données.

Pas une réponse, mais une URL mise à jour à la documentation « Importation de données Efficacement »

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html#//apple_ref/doc/uid/TP40003174-SW1

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top