CoreData - NSTreeController

  • CoreData - NSTreeController

    Hallo,
    ich habe ein Entity "Page". siehe unten das bild.
    nun habe ich im IB einen NSTreeController erstellt. den entity nam auf "Page" gestellt, "automatically prepares content" aktiviert, "children keyPath" steht auf "subPages", der managedObjectContext ist auch gebunden. Dann hab ich noch plus und minus buttons, die mit dem controller verbunden sind. Und ich habe einen outlineview, dessen column an "name" und "arrangedObjects" vom controller gebunden ist.
    alles auch speichern läuft wunderbar, nur wenn ich auf den plus button klicke, werden zwei Pages erzeugt. deaktiviere ich "automatically prepares content", dann klappt es, aber es wird nix gespeichert!

    Was mache ich falsch? wie löst man das? ich bin echt genervt.!
  • RE: CoreData - NSTreeController

    Original von hanswurst
    Was mache ich falsch? wie löst man das? ich bin echt genervt.!


    Dem schließe ich mich an, so etwas versuche ich auch seit zwei Tagen. :evil:

    Mit ner zweiten Entity "Container", die (ausgenommen der superPage-Relation) der Entity "Page" identisch ist, klappt es...
    (Im NSTreeController den EntityName auf "Container" setzen)

    Aber das will ich so ja eigentlich gar nicht haben...
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • RE: CoreData - NSTreeController

    Nur eine Mutmaßung, weil ich eine entsprechende Struktur programmatisch erzeuge und daher damit keine Erfahrung habe.

    Wenn ein Subobjekt erzeugt wird, so wird der Rückverweis auf die Eltern gesetzt. Um Inkonsistenzen im Model zu vermeiden, wird dabei automatisch das Kind-Objekt dem Eltern-Objekt hinzugefügt. Um also etwa ein Kind hinzuzufügen, reicht ein:

    Quellcode

    1. [child setValue:parent forKey:@"parent"]
    Wenn man nun den Controller auch noch den Content des Eltern-Objektes erzeugen lässt, wird mutmaßlich ein weiteres Objekt (dasselbe?) in die Relationship hinzugefügt. Jetzt haben wir zwei.

    Insofern wäre das Verhalten nachvollziehbar, wenn auch nicht gerade erwartet.

    Aber nur eine Mutmaßung ...
    +++
    Ah, dasselbe kann es nicht sein, da die Relationship ja in einem NSSet-Objekt landet. Das akzeptiert aber keine Doppel.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Nee, eigentlich kann das so nicht sein.
    Denn der Array-Controller zeigt den Inhalt an, den ich gesetzt habe.
    Lediglich das OutlineView bildet alles mehrfach ab.

    Aus jedem Child macht er dann quasi ein Parent.
    Ich hab da mal ein Bildchen angehängt.

    Hier sieht man wunderschön, was der Jung da verkehrt macht.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Tom:
    Er zeigt alle sechs Objekte des Modells an.
    So wie es auch das OutlineView tun sollte.
    Das zeigt aber jedes Objekt immer und immer wieder an.
    Im Modell existieren nur diese 6 Objekte.

    Es ist das 08-15 Binding, welches erstellt wird, wenn du die Entität aus dem Datenmodell auf ein leeres Fenster im IB ziehst und 'Many Objects' wählst.

    Lösung gefunden!

    In den Attributes des NSTreeController muss folgendes Predicate gesetzt werden:

    Quellcode

    1. parent == nil


    Wobei parent halt die Relationship zum Elternelement ist, in deinem Fall also superPage.

    Quellcode

    1. superPage == nil


    Warum nicht gleich so? +hmpf+
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Trotzdem interessiert mich, wie du das gebunden hast?

    Diese Lösung ist ja etwas schräg. Erst werden alle Instanzen der Entität genommen und dann wiederum diejenigen herausgefiltert, die nicht die obersten sind. Das ist ja ein bisschen von hinten durch die Brust ins Auge.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Es ist das 08-15 Binding, welches erstellt wird, wenn du die Entität aus dem Datenmodell auf ein leeres Fenster im IB ziehst und 'Many Objects' wählst.

    NSArrayController
    Entity -> Elements
    Automatically prepares content

    MOC bindTo: MyApp_AppDelegate
    Model Key Path: managedObjectContext

    NSTreeController
    Entity -> Elements
    Automatically prepares content
    children Keypath: elemente
    Predicate: parent == nil

    MOC bindTo: MyApp_AppDelegate
    Model Key Path: managedObjectContext

    08-15, halt.

    Original von Tom9811
    Erst werden alle Instanzen der Entität genommen und dann wiederum diejenigen herausgefiltert, die nicht die obersten sind. Das ist ja ein bisschen von hinten durch die Brust ins Auge.


    Nee, eigentlich nicht...
    Ich denke eher, es werden alle Instanzen der Entität genommen und jede unter brutalster Auflösung der parent-Relationship durchlaufen, damit jede Instanz einmal ohne parent dasteht, also auch mal 'root' sein darf..
    Das Predicate sorgt nur dafür, dass auch wirklich diejenigen als parent auftauchen, die keine parent-Relationship haben, also quasi wirklich 'root' sind.

    (und wenn schon von hinten durch die Brust ins Auge - hauptsache kampfunfähig. ;))
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Dann wurndert mich das nicht.

    Wenn du es so ziehst, dann "ziehst du alle Instanzen der Entität" in das Window. Damit hast du alle Objekte drin, auch die Subobjekte. Dieses Verhalten ist gewollt und erwartet. Bei anderen Controllern, insbesondere NSArrayControllern, ist das auch praktisch.

    Wenn du aber eine Reflexivverknüpfung hast, ist es unpraktisch. Denn dann willst du gar nicht alle Objekte auf oberster Ebene haben, sondern nur die Root-Objekte. Du erzielst jetzt die Kampfunfähigkeit dadurch, dass du es der Liste "sämtlicher Instanzen" die richtigen herausfischst, also gleichermaßen den Fehler nachträglich wieder korrigierst.

    OHNE EINEN FLAME ZU BEGINNEN:
    Das liegt daran, dass du Core Data wie eine Datenbank mit einer Tabelle aller Instanzen benutzt. Das *muss* dann so ausgehen. Und in der Tat würde man es in einer Datenbank exakt so lösen, wie du es gemacht hast.

    Wenn du aber nun einen Graphen daraus machst, also gedanklich, dann taucht das Problem erst gar nicht auf. Speicher einfach die Root-Objekte in einer anderen Entität (keine Ahnung, wie die bei dir sinnvoll heißt) und binde daran! Ode einfacher: Fisch dir die Root-Instanz(en) anders heraus und lege einen Schlüssel darauf. Dann erhältst du von vornerein nur die Root-Objekte. Das sind automatisch die richtigen. Da muss nichts mehr gefiltert werden. Oder anders: Benutze Core Data wie die Modellierung eines Objektgraphen, dann taucht das Problem gar nicht erst auf und es verhält sich so, wie du es erwartest.

    Anders: Mach es halt so, wie du es schon seit Jahren vor Core Data gemacht hast. Core Data verhält sich gar nicht anders als ein klassischer Objekt-Graph, wenn man ihm die Chance gibt, sich so zu verhalten.

    Beispiel bei mir:
    Ich habe Threads und dazu eine Hierarchie von Postings. Jeder Thread hat ein Root-Posting. Dieses ist mein Startpunkt. Core Data kommt gar nicht erst auf den Gedanken, irgendwelche Subpostings oder Postings aus anderen Threads anzuzeigen, auch ohne dass ich mit Prädikaten die richtigen aus dder "Tabelle" wieder herausfische. Es funktioniert einfach richtig und wie erwartet.

    Nein, dazu muss jetzt keine Diskussion losgetreten werden.

    [Dieser Beitrag wurde mehrfach editiert -- Tom]
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von hanswurst
    ich melde mich aber schon jetzt wieder. und zwar, wenn ich jetzt drei pages a, b und c hinzufüge (bild 1) und dann b entferne, bleibt c übrig und ist nicht entfernt (bild 2)


    deleteRule im subPages - Relationship auf 'cascade' gestellt? ;)
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Original von Lucas de Vil
    Original von hanswurst
    ich melde mich aber schon jetzt wieder. und zwar, wenn ich jetzt drei pages a, b und c hinzufüge (bild 1) und dann b entferne, bleibt c übrig und ist nicht entfernt (bild 2)


    deleteRule im subPages - Relationship auf 'cascade' gestellt? ;)


    treffer :)

    was heist eigentlich delete rule?
  • Original von Tom9811
    Speicher einfach die Root-Objekte in einer anderen Entität (keine Ahnung, wie die bei dir sinnvoll heißt) und binde daran! Dann erhältst du von vornerein nur die Root-Objekte.


    Na, aber genau das _will_ ich doch gar nicht, weil ich eben nicht weiß, ob das Objekt später ein Root-Objekt sein soll oder nicht!

    Meine Idee zur CoreData-Übung war eine Art ToDo-Liste.
    Da mir ein einfaches unverschachteltes ToDo-Element zu langweilig war, wollte ich es so einrichten, dass ein ToDo-Element unterschiedliche Sub-Elemente enhalten kann, aber nicht muss.

    Bei der von dir vorgeschlagenen Version (welche ich zuerst probiert habe), ist ja jedes Element quasi gezwungen, ein Subelement zu enthalten. (auch wenn man ihm keines gibt)
    Vergleiche dazu meinen ersten Post hier im Thread.

    Da ist mir dann allerdings keine bessere Variante eingefallen als dieses Durcheinander, habe ich halt dieses versucht.

    Mir persönlich ist einfach nicht klar, wieso unser OutlineView bzw. der TreeController (wer auch immer jetzt dafür verantwortlich ist) eben alles mehrfach anzeigt.

    Es gibt tatsächlich nur diese dort gezeigten 6 Elemente.
    Das weiß ich rein zufällig ganz genau, weil ich nur 6x auf den Add-Button geklickt habe.
    Ich habe ihn nie darum gebeten, daraus 1x6+1x5+1x4+1x3+1x2+1x1 Objekt anzuzeigen.

    Dieses Verhalten ist mir total unerklärlich, wenngleich es für dich logisch erscheinen mag. Ich verstehe es jedenfalls absolut nicht.

    Also, mein Problem ist halt, dass ich keine Ahnung habe welches Modell ich brauche, wenn ich
    identische Elemente sichern will,
    a) die ganz viele Kindelemente haben können (aber nicht müssen)
    b) die ein Elternelement haben können (aber nicht müssen)
    c) bei denen ich im Vorfeld nicht weiß, ob sie Kind- oder Elternelemente haben werden
    d) deren Anzahl an Kind- und Elternelementen sich beliebig ändern können

    Wenn du mir da ein vernünftigeres Modell vorschlagen kannst, wäre ich dir sehr dankbar.

    Ich gehe in meiner Naivität allerdings davon aus, dass es dafür kein vernünftigeres Modell gibt und diese Predicate-Lösung extra für solche Probleme eingeführt wurde.

    Denn Fakt ist: da sowohl der NSArrayController als auch der NSTreeController haargenau dasselbe Binding haben, nämlich den managedObjectContext von MyApp_AppDelegate, greifen sie auch auf haargenau dieselben Instanzen zu.

    Da im TableView des NSArrayController nur 6 Instanzen vorkommen, im OutlineView des NSTreeController allerdings jede Instanz, die ein Elternelement hat, solange um eine Ebene nach oben geschoben wird, bis sie kein Elternelement mehr hat, suche ich den 'Fehler' im OutlineView.

    Denn ändere ich den Titel '1.1.1.1.1.1' in 'Letzter' steht wie von Zauberhand wirklich _überall_ statt '1.1.1.1.1.1' plötzlich 'Letzter'. Lösche ich ein '1.1.1.1.1' raus verschwinden alle '1.1.1.1.1' sowie sämtliche darunter liegenden Elemente.

    Ich habe ihn mit keinem Wort gebeten, dass er sich so verhalten soll.
    Wie denn auch, ich weiß ja nicht einmal, wer sich so verhält - geschweige denn warum.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Diese delete rule ist eine Löschregelung (wer hätte das gedacht?)
    Damit definierst du, was mit den Elementen deiner Relationship passiert, nachdem du das Parent entfernt hast.

    Bei NULLIFY wird die betreffende Relationship des Kindelementes auf NULL gesetzt.
    CASCADE führt eine Lösung der Kindelemente durch.
    DENY klingt für mich so, als weigere sich das Kindelement, die Löschung seines Elternelementes zu akzeptieren.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P