Transférer un objet d'une relationnship à  l'autre dans Core Data

Bonsoir,



J'essaie de transférer un objet contenu dans le NSSet d'une entité vers une autre.



Les logs montrent que "ça marche" (l'objet disparaà®t d'un set et apparaà®t dans l'autre) mais j'ai des messages alarmants dans la console (voir ci-dessous). Quelle est l'erreur? Dois-je informer le KVO que j'ai effectué une modification? Merci de m'aider à  déchiffrer ces messages:





2012-09-13 22:46:46.865 AutoText[4548:303] -[_NSFaultingMutableSet objectAtIndex:]: unrecognized selector sent to instance 0x10afbcf10

2012-09-13 22:46:46.865 AutoText[4548:303] -[_NSFaultingMutableSet objectAtIndex:]: unrecognized selector sent to instance 0x10afbcf10

2012-09-13 22:46:46.868 AutoText[4548:303] (

0 CoreFoundation 0x00007fff872ef0c6 __exceptionPreprocess + 198

1 libobjc.A.dylib 0x00007fff900ed3f0 objc_exception_throw + 43

2 CoreFoundation 0x00007fff8738570a -[NSObject(NSObject) doesNotRecognizeSelector:] + 186

3 CoreFoundation 0x00007fff872dd5ee ___forwarding___ + 414

4 CoreFoundation 0x00007fff872dd3d8 _CF_forwarding_prep_0 + 232

5 AppKit 0x00007fff90783dc5 -[NSSelectionBinder observeValueForKeyPath:ofObject:change:context:] + 148

6 Foundation 0x00007fff89975860 NSKeyValueNotifyObserver + 390

7 Foundation 0x00007fff8996fd23 -[NSObject(NSKeyValueObservingPrivate) _notifyObserversForKeyPath:change:] + 967

8 AppKit 0x00007fff9076ee53 -[NSController _notifyObserversForKeyPath:change:] + 209

9 AppKit 0x00007fff9076ece6 -[NSArrayController didChangeValuesForArrangedKeys:objectKeys:indexKeys:] + 126

10 AppKit 0x00007fff9076e92f -[NSArrayController _selectObjectsAtIndexesNoCopy:avoidsEmptySelection:sendObserverNotifications:forceUpdate:] + 613

11 AppKit 0x00007fff908955b9 -[NSArrayController _modifySelectedObjects:useExistingIndexesAsStartingPoint:avoidsEmptySelection:addOrRemove:sendObserverNotifications:forceUpdate:] + 976

12 AppKit 0x00007fff905b8e31 -[NSArrayController setSelectedObjects:] + 47

13 AutoText 0x0000000100002a62 -[ATXTDocument clickedWord:] + 802

14 AutoText 0x00000001000026f6 -[ATXTDocument textView:willChangeSelectionFromCharacterRange:toCharacterRange:] + 406

15 AppKit 0x00007fff906f0de4 -[NSTextView(NSSharing) setSelectedRanges:affinity:stillSelecting:] + 1076

16 AppKit 0x00007fff90b7b8b5 -[NSTextView mouseDown:] + 11017

17 AppKit 0x00007fff9072358e -[NSWindow sendEvent:] + 6853

18 AppKit 0x00007fff9071f6c4 -[NSApplication sendEvent:] + 5761

19 AppKit 0x00007fff906352ea -[NSApplication run] + 636

20 AppKit 0x00007fff905d9ca6 NSApplicationMain + 869

21 AutoText 0x0000000100001442 main + 34

22 AutoText 0x0000000100001414 start + 52

23 image/huh.gif' class='bbc_emoticon' alt='???' /> 0x0000000000000003 0x0 + 3

)

Réponses

  • CéroceCéroce Membre, Modérateur
    Sans code, nous ne pouvons pas grand chose pour toi.

    Par contre, on dirait quand même qu'il y a un mélange entre NSArray et NSSet. Vérifie que le NSArrayController est bien bindé sur un NSSet.
  • Je comprends. Voici le code:




    [font=Courier]<br />
    - ([color=#c91b00][b]IBAction[/b][/color]) takeObject:([color=#c91b00][b]id[/b][/color])sender{[/font][font=Courier]<br />
        NSManagedObject *thePlayer = [playerController selection];[/font][font=Courier]<br />
        NSManagedObject *theCard = [[cardArrayController selectedObjects]objectAtIndex:[color=#009dbc][b]0[/b][/color]];[/font][font=Courier]<br />
        NSManagedObject *theObj = [[objectArrayController selectedObjects]objectAtIndex:[color=#009dbc][b]0[/b][/color]];[/font][font=Courier]<br />
        [[theCard valueForKey:[color=#1b9447][b]@&quot;objects&quot;[/b][/color]]removeObject: theObj];[/font][font=Courier]<br />
        [[thePlayer valueForKey:[color=#1b9447][b]@&quot;objects&quot;[/b][/color]]addObject: theObj];[/font][font=Courier]<br />
        [cardArrayController selectCard:theCard];[/font][font=Courier]<br />
        NSLog([color=#1b9447][b]@&quot;CARD HAS %@&quot;[/b][/color],[theCard valueForKeyPath:[color=#1b9447][b]@&quot;objects.alias&quot;[/b][/color]]);[/font][font=Courier]<br />
        NSLog([color=#1b9447][b]@&quot;PLAYER HAS %@&quot;[/b][/color],[thePlayer valueForKeyPath:[color=#1b9447][b]@&quot;objects.alias&quot;[/b][/color]]);[/font][font=Courier]<br />
    }[/font]<br />
    <br />
    


    Je précise que les logs sont corrects.
  • Je pense qu'il faut réaffecter la relation entière quand tu la modifies. En tous cas, c'est comme ça que je fais.


    <br />
        NSMutableSet* set = [NSMutableSet setWithSet:thePlayer.objects];<br />
        [set addObject:theObj];<br />
        thePlayer.objects = set;<br />
    




    En fait, tu peux demander à  Xcode de générer le code pour tes objets. Menu Editor|Create NSManagedObject Subclass...

    Normalement, il doit te générer le code pour modifier tes relations proprement.
  • Merci pour la réponse. En fait, j'essaie d'éviter de sous-classer les entités Core Data. En termes plutôt sibyllins, Apple déconseille de le faire (c'est vrai que ça a l'air de se compliquer rapidement après " en admettant que les débuts soient simples).



    Après avoir fouillé longuement, j'ai découvert que




    [color=#933A2C][font=Courier]<br />
    [color=#000000]    [[theCard [/color][b][i]mutableSetValueForKey[/i][/b][color=#000000]:[/color][color=#1b9447][b]@&quot;objects&quot;[/b][/color][color=#000000]][/color][b][i]removeObject[/i][/b][color=#000000]: theObj];[/color][/font][/color][color=#933A2C][font=Courier]<br />
    [color=#000000]    [[thePlayer [/color][b][i]mutableSetValueForKey[/i][/b][color=#000000]:[/color][color=#1b9447][b]@&quot;objects&quot;[/b][/color][color=#000000]][/color][b][i]addObject[/i][/b][color=#000000]: theObj];[/color][/font][/color]<br />
    <br />
    


    fonctionne plus proprement (sûrement une obscure histoire de KVO). En tout cas je n'ai plus de message d'erreurs. Mais je tâtonne par essais/erreurs, faute d'une explication. Et je n'aime pas.
  • En fait je te conseillais juste de le faire pour voir le code généré et le réutiliser ensuite.

  • berfisberfis Membre
    septembre 2012 modifié #7
    Je l'ai fait, et j'ai vu le code généré (ou plutôt l'interface). Il y a des routines comme "addObjectsObject" etc. mais c'est ensuite à  nous de les implémenter en faisant très attention au KVO -- en fait, une fois que tu interviens dans Core Data, tu te retrouves à  le gérer pratiquement tout seul et dans ce cas le "bénéfice" devient mince, autant retrourner aux bons vieux NSKeyedArchievers...



    Ensuite, pour faire "machine arrière" et de défaire ce que l'éditeur a fait pour générer ta sous-classe, il y a de la joie...
  • FKDEVFKDEV Membre
    septembre 2012 modifié #8
    'berfis' a écrit:


    Je l'ai fait, et j'ai vu le code généré (ou plutôt l'interface). Il y a des routines comme "addObjectsObject" etc. mais c'est ensuite à  nous de les implémenter en faisant très attention au KVO




    Désolé, je me suis trompé, ma mémoire a flanché, je pensais qu'il te générait l'interface ET le code.

    Il te génère juste l'interface, par contre le code tu n'as pas besoin de l'écrire toi-même. Il est généré dynamiquement.



    Pour bien comprendre comment on modifie une relation, le mieux c'est de revenir à  la doc.

    La doc copiée ci-dessous, te dit que tu as seulement trois solutions pour modifier une relation :

    -soit tu affectes toute la relation comme je l'ai indiqué dans mon post plus haut,

    -soit tu utilises (et c'est plus efficace) mutableSetValueForKey qui va retourner un type special de set mutable qui va reporter les modifs dans Core Data.

    -soit tu utilises les interfaces générés dynamiquement.

    Il n'y a pas d'autres solutions.

    La méthode que tu utilisais te retourne un set que tu peux utiliser uniquement pour accéder aux éléments de la relation mais pas pour en modifier le nombre.



    "



    You can in principle manipulate an entire to-many relationship in the same way you do a to-one relationship, using either a custom accessor method or (more likely) key-value coding, as in the following example.



    NSSet *newEmployees = [NSSet setWithObjects:employee1, employee2, nil];

    [aDepartment setEmployees:newEmployees];



    NSSet *newDirectReports = [NSSet setWithObjects:employee3, employee4, nil];

    manager.directReports = newDirectReports;


    Typically, however, you do not want to set an entire relationship, instead you want to add or remove a single element at a time. To do this, you should usemutableSetValueForKey: or one of the automatically-generated relationship mutator methods (see “Dynamically-Generated Accessor Methods”):



    NSMutableSet *employees = [aDepartment mutableSetValueForKey:@employees];

    [employees addObject:newEmployee];

    [employees removeObject:firedEmployee];



    // or

    [aDepartment addEmployeesObject:newEmployee];

    [aDepartment removeEmployeesObject:firedEmployee];




    It is important to understand the difference between the values returned by the dot accessor and by mutableSetValueForKey:.mutableSetValueForKey: returns a mutable proxy object. If you mutate its contents, it will emit the appropriate key-value observing (KVO) change notifications for the relationship. The dot accessor simply returns a set. If you manipulate the set as shown in this code fragment:



    [aDepartment.employees addObject:newEmployee]; // do not do this!


    then KVO change notifications are not emitted and the inverse relationship is not updated correctly.

    Recall that the dot simply invokes the accessor method, so for the same reasons:



    [[aDepartment employees] addObject:newEmployee]; // do not do this, either!


    "
  • Merci, FKDEV, pour cette réponse rapide, exacte et complète.
Connectez-vous ou Inscrivez-vous pour répondre.