CoreData Relationship (many to many)

AlakAlak Membre
octobre 2012 modifié dans Objective-C, Swift, C, C++ #1
Bonjour,



J'essaye d'ajouter une relation entre deux objets, et je me prend un :
<br />
*** Terminating app due to uncaught exception &#39;NSInvalidArgumentException&#39;, reason: &#39;*** -[NSSet intersectsSet:]: set argument is not an NSSet&#39;<br />








Le model :



<br />
@interface Pictograms : NSManagedObject<br />
@property (nonatomic) BOOL enabled;<br />
@property (nonatomic) BOOL humeur;<br />
@property (nonatomic) BOOL lock;<br />
@property (nonatomic, retain) NSString * name;<br />
@property (nonatomic) int16_t used;<br />
@property (nonatomic, retain) NSSet *categories;<br />
@property (nonatomic, retain) Sounds *sound;<br />
@property (nonatomic, retain) Images *image;<br />
@end<br />
@interface Pictograms (CoreDataGeneratedAccessors)<br />
- (void)addCategoriesObject:(Categories *)value;<br />
- (void)removeCategoriesObject:(Categories *)value;<br />
- (void)addCategories:(NSSet *)values;<br />
- (void)removeCategories:(NSSet *)values;<br />
@end<br />






La method utilisé ajouté grâce a une "category" :
<br />
- (void)addCategory:(Categories *)category {<br />
	[self addCategoriesObject:category];<br />
	Categories *sock = [Categories getSockCategory];<br />
	if (self.categories.count &gt; 0 &amp;&amp; [self.categories containsObject:sock]) {<br />
		[self removeCategoriesObject:sock];<br />
	}<br />
}<br />






l'erreur arrive à  :
<br />
[self addCategoriesObject:category];<br />




Je suis quasiment sûr que mon objet category est bien du type (Categories *), avant d'exécuter la méthode je fait une vérification pour être sûr que category ne fait pas déjà  partie des catégories de ce pictogramme.



Si ca peux aider, j'ai modifié la base SQLite derrière mon CoreData pour la pré-remplir. Mais jusqu'ici la base pré-rempli fonctionnais très bien.



Je ne comprend pas bien l'erreur, car elle parle de NSSet mais la méthode prend en argument un objet de type "Categories" et c'est elle qui est sensé l'ajouter au NSSet.



Merci d'avance pour votre aide.
«1

Réponses

  • FKDEVFKDEV Membre
    octobre 2012 modifié #2
    C'est peut-être le moment de renommer des classes pour les mettre au singulier.

    Pictograms -> Pictogram

    Categories -> Category.



    Du coup cela donnera :

    -(void)addCategoriesObject: (Category*)value;

    -(void)addCategories: (NSSet*)values;



    A mon avis le bug n'est pas dans le code affiché ici.

    Peut-être dans la définition du model, vérifie que tu n'as pas coché "ordered set" par exemple.

    Sinon peux-tu fournir un peu plus de details sur la pile d'appel ?
  • Petite question : n'aurais-tu pas un ordered relationship quelque part ?

    Petite remarque : Mieux vaut nommer les entités au singulier. En effet, tu défini dans ton modèle un object et non pas une collection.
  • Ok pour le renommage, j'ai toujours un doute quand je crée un model. Maintenant je ferais attention.
  • Donc j'ai renommer mes entities au singulier. Et j'ai vérifié que je n'avais pas d'[font=helvetica, arial, sans-serif]ordered relationship[/font].

    Une autre idée?
  • AlakAlak Membre
    octobre 2012 modifié #6
    Voici la method qui appel cette method :


    <br />
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {<br />
    	Category *c = [self.categories objectAtIndex:indexPath.row];<br />
    	CategoriesManagerCell *cell = (CategoriesManagerCell *)[self.tableView cellForRowAtIndexPath:indexPath];<br />
    	if ([self.pictogram.categories containsObject:c]) {<br />
    		[self.pictogram removeCategory:c];<br />
    		cell.tickBoxIV.image = [UIImage imageNamed:@&quot;tickBoxNormal.png&quot;];<br />
    	} else {<br />
    		[self.pictogram addCategory:c];<br />
    		cell.tickBoxIV.image = [UIImage imageNamed:@&quot;tickBoxTicked.png&quot;];<br />
    	}<br />
    }<br />
    



    <br />
    - (void)addCategory:(Category *)category {<br />
    	[self addCategoriesObject:category];<br />
    	Category *sock = [Category getSockCategory];<br />
    	if (self.categories.count &gt; 0 &amp;&amp; [self.categories containsObject:sock]) {<br />
    		[self removeCategoriesObject:sock];<br />
    	}<br />
    }<br />
    - (void)removeCategory:(Category *)category {<br />
    	[self removeCategoriesObject:category];<br />
    	if (self.categories.count &lt; 1) {<br />
    		Category *sock = [Category getSockCategory];<br />
    		[self addCategoriesObject:sock];<br />
    	}<br />
    }<br />
    




    Après vérification, je suis sûr de bien passer un objet "Category" valide :/
  • Juste pour voir, as-tu essayé directement les mutable sets du genre :



    NSMutableSet *theCategories : [self mutableSetValueForKey:@categories];

    [theCategories addObject:category];



    J'ai voulu vérifier dans mes codes comment je faisais mais je me suis rendu compte que je n'avais aucun many-to-many...
  • Je test de suite.
  • Donc j'ai la même erreur en faisant :


    <br />
    NSMutableSet *cat = [[NSMutableSet alloc] initWithSet:self.categories];<br />
        [cat addObject:category];<br />
        self.categories = cat;<br />
    
  • Bizarre, je viens de créer un projet et je n'ai pas de soucis quand je fais un add sur un many-to-many relationship.

    Mais dis moi, tes methodes addCategory et removeCategory ne seraient-elles pas dans un fichier extension ?
  • Dans une "category" de ma classe Pictograms. Mais je pense avoir une piste. Avant d'avoir ma BDD pré rempli, je le faisait dans l'appDelegate et ça marchai bien. Donc je dois avoir un problème dans mon SQLite. Je vais revoir ça. Merci de ton aide.
  • 'Alak' a écrit:


    Je ne comprend pas bien l'erreur, car elle parle de NSSet mais la méthode prend en argument un objet de type "Categories" et c'est elle qui est sensé l'ajouter au NSSet.




    Peut-être qu'en interne, CoreData inclue ton objet dans NSSet pour passer par une méthode générique de fusion des NSSet (d'où intersectrect...). C'est pour essayer de comprendre un peu ça qu'il serait bien de voir une plus grande partie de la pile d'appel.



    Tu n'utilises bien qu'un seul managedObjectContext pour récupérer tous tes managedObjects ?
  • FKDEVFKDEV Membre
    octobre 2012 modifié #13
    'Alak' a écrit:


    Si ca peux aider, j'ai modifié la base SQLite derrière mon CoreData pour la pré-remplir. Mais jusqu'ici la base pré-rempli fonctionnait très bien.




    C'est quand même assez osé de faire ça.

    Peux-tu être certain que tes bases sont correctes ?



    Par exemple, est-ce que tu peux partir d'une base vierge, créer un objet de chaque type (Pictogram et Category) manuellement et les lier en utilisant ta méthode ? Ensuite tu fais un save. Tu fais ça dans le applicationDidFinishLaunching par exemple.

    Si cela fonctionne, tu fais pareil en partant de ta base pré-remplie.

    Si cela fonctionne, tu fais pareil mais en prenant deux objets issus de ta base pré-remplie.
  • 'Alak' a écrit:


    Dans une "category" de ma classe Pictograms. Mais je pense avoir une piste. Avant d'avoir ma BDD pré rempli, je le faisait dans l'appDelegate et ça marchai bien. Donc je dois avoir un problème dans mon SQLite. Je vais revoir ça. Merci de ton aide.




    Voilà , c'est ce que je voulais dire au-dessus.



    Tu gagneras sans doute du temps à  générer ta base via du code dans ton application. Un code que tu ferai tourner dans le simulateur pour créer ta base initiale via CoreData.
  • AlakAlak Membre
    octobre 2012 modifié #15
    Enfaite la base marche très bien pour tout le reste et j'ai vraiment beaucoup d'entrés. (plus de 300)

    Je fais les testes pour voir ou est le problème.
  • AlakAlak Membre
    octobre 2012 modifié #16
    Bon ça marche plus même sans la base pré-rempli. Rien de rien. Que j'essaye d'ajouter un NSSet ou un objet seul toujours la même erreur.





    J'ai l'impression que les
    <br />
    CoreDataGeneratedAccessors<br />
    
    ne fonctionne pas correctement.
  • Résolut :



    Dans ma "category" j'avais la méthode ci-dessous :


    <br />
    - (NSArray *)getPictograms {<br />
        NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:@&quot;used&quot; ascending:YES] autorelease];<br />
        NSArray *arr = [self.pictograms sortedArrayUsingDescriptors:@[sort]];<br />
        return arr;<br />
    }<br />
    




    Hors une méthode avec le même nom doit être automatiquement généré par CoreData.
  • C'était exactement ce à  quoi je pensais quand j'ai écrit :



    "[font=helvetica, arial, sans-serif]Mais dis moi, tes methodes addCategory et removeCategory ne seraient-elles pas dans un fichier extension ?"[/font]



    image/cliccool.gif' class='bbc_emoticon' alt=' :p ' />
  • CéroceCéroce Membre, Modérateur

    Déterrage: je n'avais encore jamais utilisé les Ordered Set avec Core Data... et je suis tombé sur le même problème.


    Ah, au fait, il s'agit d'un bug d'Apple. Qui traà®ne depuis plus de deux ans.


     


    Et y'a un gars sympa qui a corrigé tout ça de façon relativement propre.


  • paozpaoz Membre
    juin 2014 modifié #20

    Je continue ton déterrage car j'ai une question.


     


    Je viens de suivre le tuto ici : http://www.raywenderlich.com/934/core-data-tutorial-for-ios-getting-started


     


    Il indique que si (par exemple) j'ai une classe SousProduit qui a une clé externe vers Produit, de rajouter un relationship dans la classe SousProduit. Jusque là  c'est logique.


    Par contre il dit aussi qu'Apple précaunise de faire le contraire aussi, c'est à  dire une clé sousProduit dans Produit.


    Or là  c'est complètement fou pour moi (je fais beaucoup d'UML donc ca me choque!).


    Est-ce que quelqu'un peut me confirmer que j'ai raison svp ? :)


    A moins que je n'aie rien compris à  CoreData !


  • KubernanKubernan Membre
    juin 2014 modifié #21

    CoreData n'est pas un gestionnaire de base de données dans le sens usuel. D'ailleurs on ne parle pas de clés (on ne définit pas une primary key par exemple) dans CoreData.


    C'est un graphe d'objets.


    Les relations inverses (si c'est bien cela dont tu parles) permettent à  CoreData d'assurer l'intégrité des données à  ta place.


  • CéroceCéroce Membre, Modérateur

    Il indique que si (par exemple) j'ai une classe SousProduit qui a une clé externe vers Produit,

    C'est pas une clef externe, c'est un pointeur, nuance. Core Data t'affranchis de la base de données. Tu vois un modèle objet. Rien ne dit qu'une clef externe est associée à  chaque relation dans la BdD.

    Par contre il dit aussi qu'Apple préconise de faire le contraire aussi, c'est à  dire une clé sousProduit dans Produit.
    Or là  c'est complètement fou pour moi (je fais beaucoup d'UML donc ca me choque!).
    Est-ce que quelqu'un peut me confirmer que j'ai raison svp ? :)

    Je confirme que tu as tort! Apple t'oblige à  nommer la relation inverse, tu auras même un warning si tu ne le fais pas. Maintenant, je t'avoue que moi aussi, je trouve ça inhabituel, mais c'est bien pratique.
  • paozpaoz Membre
    juin 2014 modifié #23

    Merci pour les deux réponses.


    Il faut que je m'éloigne d'UML :)


     


    Donc pour faire en sorte que les deux se "comprennent", je mets la bonne "destination" pour les deux relationships.


    A quoi sert alors le champ "Inverse" ?


     


    EDIT : Je viens de voir sur le schéma qu'au lieu de faire 2 flèches différentes, il combine les deux. Mais quelle est la différence entre les deux ?


  • KubernanKubernan Membre
    juin 2014 modifié #24


    Merci pour les deux réponses.


    Il faut que je m'éloigne d'UML :)


     


    Donc pour faire en sorte que les deux se "comprennent", je mets la bonne "destination" pour les deux relationships.


    A quoi sert alors le champ "Inverse" ?


     


    EDIT : Je viens de voir sur le schéma qu'au lieu de faire 2 flèches différentes, il combine les deux. Mais quelle est la différence entre les deux ?




    En fait tu fais bien deux flèches (deux relations), et c'est en indiquant que la seconde est inverse de la première que XCode n'en fait qu'une du genre <---> selon la cardinalité. Tu peux également faire une relation d'une entité sur elle-même.


     


    On peut imaginer que Produit pointe sur lui même pour les sous produits (un sous produit n'est-il pas un produit en fin de compte ?).


     


    La différence entre les deux quoi ?


    C'est à  toi d'indiquer le type de relation (to-one, two-many). Ainsi pour reprendre ton exemple Produit, tu auras Produit<---->>SousProduit (i.e. à  un Produit correspond plusieurs SousProduit, et un SousProduit correspond à  un et un seul Produit).


    Dans tes relations tu exprimes aussi les Delete Rules. En spécifiant par exemple Cascade à  la relation Produit---->>SousProduit ; si dans ton code tu supprimes un Produit, alors CoreData va automatiquement supprimer les SousProduit qui lui sont associés (ce n'est pas forcément efficace en terme de performances à  partir d'un volume important de données, mais c'est une autre histoire).


  • AliGatorAliGator Membre, Modérateur
    Heu en UML aussi il faut spécifier les relations inverses hein...

    Une voiture contient 4 roues, mais aussi chaque roue appartient à  une voiture. Il faut spécifier la cardinalité dans les 2 sens, certes tu n'as qu'un trait entre les 2 entités en UML, pour indiquer une composition dans le cas que j'ai pris comme exemple, mais tu as une cardilaité de chaque côté quand même. Si tu dis juste que chaque roue appartient à  une voiture sans préciser la cardinalité inverse tu oublies dans ton schema UML de spécifier que chaque voiture a exactement 4 roues. Et de spécifier aussi ce qui se passe si la voiture est détruite, est-ce que les roues le sont aussi, et vice-versa (Delete Rules).

    De même si tu ne mets une cardinalité que d'un côté entre une entité "Personne" et une entité "Voiture", tu vas spécifier dans ton UML que une Maison peut être habitée par 0 ou plusieurs personnes (0-*), mais il faut aussi préciser en UML comme en CoreData si une personne peut avoir 0 maisons, ou qu'une seule, ou plusieurs. Bah les relations inverses, c'est tout à  fait ça.
  • KubernanKubernan Membre
    juin 2014 modifié #26

    Heuuu Ali je n'ai pas dit le contraire. J'ai supposé qu'il parlait de la différence entre deux relations dans CoreData. Pas entre UML et CoreData. J'ai donc développé sur les options Core Data.


  • colas_colas_ Membre
    juin 2014 modifié #27
    La galère avec les relations inverses c'est quand on utilise des sous entités. Si quelqu'un a des idées, je suis preneur !

    Exemple :


    Voiture avec deux sous-entités : voiture de course et voiture normale.

    Autre entité : Conducteur.


    Je veux une relation conducteurDeCourse et une relation conducteurNormal sur Conducteur.

    Je veux une relation voiture sur Conducteur.


    Bon mon cas est un peu débile, mais j'ai été confronté à  ce cas dans des cas concrets.


    Si quelqu'un a déjà  été confronté à  cette question et à  des idées...
  • J'évite autant que possible d'utiliser des entités abstraites. C'est de cela qu'il s'agit quand tu parles de sous entités ?

    Pourquoi ne pas créer une seule entité Voiture avec un attribut typeVoiture ?
  • Non ce n'est pas forcément abstrait. à‰coute, ce qui est fait est fait, mais avoir de l'héritage est vraiment un plus. Imagine que tu as une

    Méthode qui doit répondre différemment selon le type de voiture.


    Pourquoi évites-tu les entités abstraites ?
  • AliGatorAliGator Membre, Modérateur
    @Kubernan je sais bien que tu n'as pas dit le contraire, je ne faisais qu'expliquer autrement à  paoz.


    @colas2 oui j'ai déjà  été confronté au problème, il n'y a pas de solution miracle à  ma connaissance
  • KubernanKubernan Membre
    juin 2014 modifié #31


    Non ce n'est pas forcément abstrait. à‰coute, ce qui est fait est fait, mais avoir de l'héritage est vraiment un plus. Imagine que tu as une

    Méthode qui doit répondre différemment selon le type de voiture.




     


    Je m'en sortirai très bien avec une méthode qui accepte un enum en paramètre pour indiquer le type de voiture.


     




    Non ce n'est pas forcément abstrait. à‰coute, ce qui est fait est fait, mais avoir de l'héritage est vraiment un plus. Imagine que tu as une

    Méthode qui doit répondre différemment selon le type de voiture.


    Pourquoi évites-tu les entités abstraites ?




     


    Parce que je trouve pas ça joli, et parce que j'ai eu de gros problèmes de performances avec. Finalement j'ai découvert après coup qu'Apple - dans une vidéo wwdc - déconseillait fortement l'usage d'entités abstraites.


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