Remplacer un NSManagedObject par un autre

Bonjour,


 


J'ai dans mes données un objet X. Je souhaite le remplacer partout où il apparaà®t par un autre Y.


 


Autrement dit, dans le "graphe de NSManagedObject", avec dedans un objet X donné, je souhaite remplacer cet objet par un autre objet Y (de la même entité, évidemment).


 


Avant de me lancer dans un code un peu bourrin, du type : parcourir l'arbre et remplacer l'objet quand on tombe dessus, je voulais savoir si vous étiez déjà  tombé sur cette problématique.


 


Merci !


 


 


Réponses



  •  


    Avant de me lancer dans un code un peu bourrin, du type : parcourir l'arbre et remplacer l'objet quand on tombe dessus, je voulais savoir si vous étiez déjà  tombé sur cette problématique.


     


    Merci !




     


    Si pour l'objet Menu (gastronomique) tu dois changer de dessert, le plus simple à  mon avis serait de :


    1- Faire un fetch de tous les menus ayant en relation le dessert X.


    2- Pour tous les menus récupérés, appliquer le nouveau dessert Y en faisant un save régulier (et non pas d'un seul coup) pour gagner en performance.


     


    Faire ça en background.

  • colas_colas_ Membre
    septembre 2013 modifié #3

    @Kuber


     


    Le problème c'est que le Dessert en question peut apparaà®tre :


    - dans les Menu


    - dans les Commande


    - dans les Stock


     


    De plus, si mon modèle évolue, ma fonction de rechercher/remplacer ne marchera plus si Dessert apparaà®t dans une nouvelle Entité.


     


    Mais merci pour ta réponse, peut-être (sûrement) qu'il y a moyen de retrouver toutes les Entités qui se réfèrent au Dessert !


  • AliGatorAliGator Membre, Modérateur
    Pourquoi u ne fetches pas le dessert à  remplacer, puis parcourres les relationship inverses pour remonter aux objets qui réfèrent à  ce dessert ?


    Si Menu à  une relationship vers Dessert alors j'espère que tu as mis une relation inverse de Dessert vers menu (cet fortement conseillé !) du coup en partant du Dessert tu peux trouver facilement tous les menus qui le contiennent, y'a pas de code à  rajouter juste à  boucler sur ta relation.
  • @Ali : très bonne idée, merci !




  • Pourquoi u ne fetches pas le dessert à  remplacer, puis parcourres les relationship inverses pour remonter aux objets qui réfèrent à  ce dessert ?


    Si Menu à  une relationship vers Dessert alors j'espère que tu as mis une relation inverse de Dessert vers menu (cet fortement conseillé !) du coup en partant du Dessert tu peux trouver facilement tous les menus qui le contiennent, y'a pas de code à  rajouter juste à  boucler sur ta relation.




     


    Attention à  la levée des faults (accès aux relationships) qui risque de grever les performances (selon le volume de données). Il faudrait - si j'ai bien compris ta proposition - faire un fetch sur Dessert. Du coup, un prefetching sur les relations (du genre menus, commandes, stocks).


    En fait, si son modèle évolue, il faudra modifier le fetch. À mon sens, dans tous les cas, une modif du modèle nécessitera une modif de sa fonction rechercher/remplacer. Ce qu'il veut éviter apparement.

  • @Kuber


     


    à  l'aide de NSRelationshipDescription et en particulier de la property inverseRelationship, on doit peut-être pouvoir rendre le parcours des relations automatiques.


     


    Que va m'apporte le prefetching ? Un seul gros accès au contexte plutôt que plein de petits accès ?


  • Quand tu fais un fetch sur une entité, core data ne met pas en mémoire les relations ; elles sont en fault. Du coup, à  chaque fois qu'on accède à  une relation, Core Data va lever le fault (accès disque) pour ramener en mémoire l'objet correspondant. Ca coûte cher.


    Le prefetching permet d'éviter dans une certaine mesure ces accès incessants et de gagner en performance.


    C'est une chose à  penser...


     


    Mais bon, pour l'optimisation faut tester différentes choses avec Core Data.


  • AliGatorAliGator Membre, Modérateur
    J'allais proposer la solution d'utiliser NSRelationShipDescription si tu veux vraiment rendre la chose dynamique.

    Quant au préfetching, il faut trouver l'équilibre.
    - Le faulting (connu aussi sous le nom de "futures") est un mécanisme qui permet d'éviter de se ramener toute la pelote de laine (si tu demandes un Dessert qu'il ne ramène pas automatiquement tous les objets liés à  Dessert alors que potentiellement tu n'en auras pas besoin, et encore moins les objets liés à  ces objets liés, et les objets liés à  ceux-là , etc... bref éviter de ramener tout le graphe au final). Donc c'est une bonne chose pour éviter des jointures inutiles dans la base et de te ramener un gros paquet de données inutiles.

    Mais d'un autre côté si tu sais que tu vas avoir besoin de ces relations, pour éviter de faire une requête pour avoir ton Dessert, puis une autre pour les Menus associés à  ce Dessert, et encore une autre pour les Commandes associées, etc... alors qu'avec une seule requête (avec jointure) dès le début, ça te ramène tout d'un coup et c'est plus efficace. Certes du coup ça te tire un peu + de données, mais en même temps tu sais que tu vas en avoir besoin.

    Bref, faut trouver l'équilibre entre ramener ce qu'il te faut pas pas non plus tout l'arbre car là  non plus les performances ne seront pas idéales si tu fais une requête qui doit te ramener + que ce qu'il te faut.
  • Voilà  ce que j'ai codé (méthode pour remplacer un objet par un autre dans un graphe CoreData)


     


    Le .h



    #import <CoreData/CoreData.h>

    @interface NSManagedObjectContext (ReplacingAnObject)

    - (BOOL)replaceThisObject:(NSManagedObject *)theOldObject
    byThisObject:(NSManagedObject *)theNewObject
    excludeRelationships:(NSArray *)namesOfRelationshipsToExclude ;

    @end


    Le .m



    #import "NSManagedObjectContext+ReplacingAnObject.h"

    @implementation NSManagedObjectContext (ReplacingAnObject)


    - (BOOL)replaceThisObject:(NSManagedObject *)theOldObject
    byThisObject:(NSManagedObject *)theNewObject
    excludeRelationships:(NSArray *)namesOfRelationshipsToExclude
    {
    if (![theNewObject.entity isKindOfEntity:theOldObject.entity])
    {
    NSLog(@Attention, vous avez essayé de remplacer un objet par un objet d'entité différente.\nOpération annulée) ;

    return NO ;
    }

    NSEntityDescription * theEntity = theOldObject.entity ;
    NSDictionary * theRelationships = theEntity.relationshipsByName ;

    for (NSString * nameRelationship in theRelationships.allKeys)
    {
    if ([namesOfRelationshipsToExclude containsObject:nameRelationship])
    {
    // do nothing
    }
    else
    {
    NSRelationshipDescription * theCurrentRelationship = theRelationships[nameRelationship] ;

    NSRelationshipDescription * theInverseRelationship = theCurrentRelationship.inverseRelationship ;

    if (theCurrentRelationship.isToMany == NO)
    {
    /***********************
    Cas des relations to-one
    ***********************/
    NSManagedObject * theConnectedObject = [theOldObject valueForKey:theCurrentRelationship.name] ;

    /*
    On fait les liaisons inverses
    (au cas où CoreData ne s'en sort pas)
    */
    [self replaceThisObject:theOldObject
    byThisObject:theNewObject
    forConnectedObject:theConnectedObject
    byInverseRelationship:theInverseRelationship] ;


    /*
    On relie le nouvel objet à  cet object
    */
    [theNewObject setValue:theConnectedObject
    forKey:nameRelationship] ;


    /*
    On retire le oldObject de sa relation avec ConnectedObject
    */
    [theOldObject setValue:nil
    forKey:nameRelationship] ;
    }
    else
    {
    /***********************
    Cas des relations to-many
    ***********************/

    id theConnectedObjects = [theOldObject valueForKey:nameRelationship] ;

    /*
    On est obligé de faire une copie de theConnectedObjects
    car au fur et à  mesure qu'on change les relations, c'est ensemble va changer !!
    */

    id theHardConnectedObjects = [theConnectedObjects copy] ;

    for (NSManagedObject * connectedObject in theHardConnectedObjects)
    {
    [self replaceThisObject:theOldObject
    byThisObject:theNewObject
    forConnectedObject:connectedObject
    byInverseRelationship:theInverseRelationship] ;
    }

    /*
    On ajoute les connectedObjects aux objets déjà  connecté à  theNewObject
    */

    if (theCurrentRelationship.isOrdered)
    {
    NSMutableOrderedSet * theObjectsConnectedToNewObject = [[theNewObject valueForKey:nameRelationship] mutableCopy] ;

    [theObjectsConnectedToNewObject unionOrderedSet:theConnectedObjects] ;

    [theNewObject setValue:theObjectsConnectedToNewObject
    forKey:nameRelationship] ;
    }
    else
    {
    NSMutableSet * theObjectsConnectedToNewObject = [[theNewObject valueForKey:nameRelationship] mutableCopy] ;

    [theObjectsConnectedToNewObject unionSet:theConnectedObjects] ;

    [theNewObject setValue:theObjectsConnectedToNewObject
    forKey:nameRelationship] ;
    }

    /*
    On retire le oldObject de sa relation avec ConnectedObject
    */
    [theOldObject setValue:nil
    forKey:nameRelationship] ;
    }
    }
    }

    /*
    On supprime le OldObject
    */
    [theOldObject.managedObjectContext processPendingChanges] ;
    [theOldObject.managedObjectContext deleteObject:theOldObject] ;

    return YES ;
    }





    - (void)replaceThisObject:(NSManagedObject *)theOldObject
    byThisObject:(NSManagedObject *)theNewObject
    forConnectedObject:(NSManagedObject *)theConnectedObject
    byInverseRelationship:(NSRelationshipDescription *)theRelationship
    {
    if (theRelationship.isToMany == NO)
    {
    [theConnectedObject setValue:theNewObject
    forKey:theRelationship.name] ;
    }
    else
    {
    id theConnectedBackObjects ;
    theConnectedBackObjects = [[theConnectedObject valueForKey:theRelationship.name] mutableCopy];

    if (theRelationship.isOrdered)
    {
    theConnectedBackObjects = (NSMutableOrderedSet *)theConnectedBackObjects ;

    /*
    On remplace l'ancien par le nouveau
    */
    [theConnectedBackObjects removeObject:theOldObject] ;
    [theConnectedBackObjects addObject:theNewObject] ;

    /*
    On sette
    */
    [theConnectedObject setValue:theConnectedBackObjects
    forKey:theRelationship.name] ;
    }
    else
    {
    theConnectedBackObjects = (NSMutableSet *)theConnectedBackObjects ;

    /*
    On remplace l'ancien par le nouveau
    */
    [theConnectedBackObjects removeObject:theOldObject] ;
    [theConnectedBackObjects addObject:theNewObject] ;

    /*
    On sette
    */
    [theConnectedObject setValue:theConnectedBackObjects
    forKey:theRelationship.name] ;
    }
    }
    }


    @end
Connectez-vous ou Inscrivez-vous pour répondre.