10 votes

Ajouter des objets uniques aux données de base

Je travaille sur une application iPhone qui récupère un certain nombre d'objets d'une base de données. J'aimerais les stocker en utilisant Core Data, mais j'ai des problèmes avec mes relations.

Un détail contient un nombre quelconque de POI (points d'intérêt). Lorsque je récupère un ensemble de POI sur le serveur, ils contiennent un ID de détail. Afin d'associer le POI au détail (par ID), mon processus est le suivant : Interroger le ManagedObjectContext pour l'ID du détail. Si ce détail existe, y ajouter le POI. Si ce n'est pas le cas, créer le détail (il a d'autres propriétés qui seront remplies paresseusement).

Le problème, c'est la performance. L'exécution de requêtes constantes vers les données de base est lente, au point que l'ajout d'une liste de 150 points d'intérêt prend une minute en raison des multiples relations impliquées.

Dans mon ancien modèle, avant Core Data (divers objets de cache NSDictionary), ce processus était très rapide (rechercher une clé dans un dictionnaire, puis la créer si elle n'existe pas).

J'ai d'autres relations que celle-ci, mais presque toutes doivent faire l'objet de cette vérification (certaines sont trop nombreuses, et elles ont un vrai problème).

Quelqu'un a-t-il des suggestions pour m'aider ? Je pourrais effectuer moins de requêtes (en recherchant un certain nombre d'identifiants différents), mais je ne suis pas sûr que cela soit très utile.

Un peu de code :

        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];
}

6voto

MattDiPasquale Points 23842

Consultez la section intitulée "Batch Faulting" sur la page intitulée "Core Data Performance" dans le manuel de Xcode. Guide de programmation des données de base que Norman a mentionné dans sa réponse.

Ne récupérer que les objets gérés dont l'identifiant est IN une collection ( NSSet , NSArray , NSDictionary ) des identifiants des objets renvoyés par le serveur peut être encore plus efficace.

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

MISE À JOUR : j'ai intégré cette astuce dans une solution pour le acani usersView . En principe, après avoir téléchargé un Réponse JSON des utilisateurs l'iPhone utilise la fonction framework JSON open source populaire pour analyser la réponse en un NSArray de NSDictionary représentant chacun un utilisateur. Ensuite, il crée un NSArray de leurs identifiants et effectue une recherche par lots sur Core Data pour voir si l'un d'entre eux existe déjà sur l'iPhone. Si ce n'est pas le cas, il les insère. Si c'est le cas, il met à jour ceux qui existent déjà uniquement si leur updated est plus ancien que celui du serveur.

4voto

RyanStochastic Points 231

J'ai réussi à faire fonctionner tout cela très bien, grâce à Norman, qui m'a mis sur la bonne voie. Je vais poster ma classe d'aide ici pour les autres.

En gros, ma classe d'aide va chercher si un NSManagedObject existe pour un certain ID, et peut le créer pour un certain ID. Cela s'exécute assez rapidement pour moi, avec 1 000 opérations de recherche/création prenant environ 2 secondes sur mon iPhone (j'ai également fait quelques autres choses, la recherche/création pure est probablement plus rapide).

Pour ce faire, il met en cache un dictionnaire de tous les NSManagedObjects et vérifie ce cache plutôt que d'exécuter une nouvelle NSFetchRequest.

Quelques modifications pourraient permettre d'accélérer encore les choses : 1. Obtenir uniquement les propriétés sélectionnées pour les NSManagedObjects 2. Ne récupérer que la propriété d'identifiant du NSManagedObject dans un dictionnaire, au lieu de l'objet entier. Lors de mes tests de performance, la requête unique n'était pas la partie la plus lente (mais avec seulement 1 000 éléments, je m'attendais à ce qu'elle soit rapide). La partie lente était la création des éléments.

  #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

3voto

Norman Points 455

Cette page propose une aide à l'optimisation des performances : http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW1

Bien que ce ne soit pas très efficace, pourquoi ne pas les construire en mémoire avec un NSDictionary ? Lisez tout ce qui se trouve dans Core Data dans un NSDictionary, puis fusionnez vos données en remplaçant tout ce qui se trouve dans Core Data.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X