Wann Core Data benutzen, wann nicht?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Wann Core Data benutzen, wann nicht?

    Hallo Leute,

    nachdem ich mich in Core Data reingestürzt habe, habe ich auch schnell gemerkt, dass es nicht ganz das Richtige für mich ist. Vor allem scheitert es bei mir an der Ordnung in den Relationships.

    Was ich haben möchte, ist der alte Klassiker:
    Zwei NSTableViews, die eine stellt die Entität Playlist dar, die zweite die Songs der gerade selektierten Playlist. Nun kann ich die Songs natürlich sortieren (z.B. nach Titel), aber die manuelle Ordnung, die der Nutzer durch beliebiges Verschieben der Songs bekommt, bekomme ich (z.B. bei Neustart) nicht so einfach wiederhergestellt. Das geht zwar über Umwege (z.B. so http://www.fatcatsoftware.com/blog/... ), aber Core Data scheint allgemein nicht dafür gemacht zu sein.

    Jetzt wollte ich mal fragen, wofür Core Data gemacht wurde, wann man sich für Core Data entscheidet und wann dagegen. Hier fehlt mir noch die Einsicht in den Stoff und der Hillegass ist auch nicht sehr hilfreich.

    Die nächste Frage wäre, falls Core Data wirklich nichts für die oben genannte Angelegenheit ist, wie setze ich an, um diese Playlist-Song-Relationship ohne Core Data umzusetzen?

    Vielen Dank für jegliche Hilfe.
  • Ich glaube das Problem ist weniger CoreData als die korrekte Anwendung des MVC-Musters.
    Selbstverständlich lässt sich so etwas mit Core Data umsetzen, ja, es ist geradezu für so etwas gemacht. Der Index ist eine explizite Modelleigenschaft und Du musst dies Deinen Entities hinzufügen.

    Auch verschiedene Playlisten mit Verweisen auf die gleichen Songs aber unterschiedlichen Indices sind kein Problem (anders als im Post aus Deinem Link angegeben):

    Entity Playlist
    -->to many-relationship entries
    -->title

    Entity Entry
    --> to one relationship song
    --> Integer index

    Entity Song
    -->String title;
    -->Data song;
  • Mit dem Hillegass Core Data zu verstehen, ist in etwa so, wie den Islam mit dem Talmut zu erklären.

    Das, was du da verlinkst, ist völlig überzogen und nicht nötig. Du benötigst im einfachsten Falle eine neue Eigenschaft.

    Darüber hinaus, will man häufiger verschiedene Ordnungen haben. Gerade Playlists sind dafür ein Beispiel: Oder sollen die Lieder in verschiedenen Playlists etwa immer an derselben Stelle stehen? Daher bietet es sich häufig an, einen neuen Entitätstypen zu kreieren.

    Das alles steht übrigens mit reichlich Code in meinem Buch.
    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"?
  • Herzlichen Dank für die Antworten!

    Ich werde mir in der Tat ein ausführlicheres Buch zum Einstieg zulegen. Der Hillegass erklärt mir (nicht nur bei Core Data) viel zu wenig.

    Zum Thema: Ich lege mir also eine zusätzliche Entität an, der den Index verwaltet. Die Tabelle wird nach diesem Index sortiert. Und den Index selbst, den muss ich nach Einfügen, Löschen und vor allem Verschieben manuell aktualisieren? Ist das richtig?

    Markus Müller schrieb:

    Ich glaube das Problem ist weniger CoreData als die korrekte Anwendung des MVC-Musters.
    Könntest du mich kurz mit der Nase draufstoßen, wie sich an dieser Stelle das MVC-Muster genau äußert (ich weiß, ich muss noch etwas bei den Grundlagen tun)?
  • Grinch schrieb:

    […]
    Zum Thema: Ich lege mir also eine zusätzliche Entität an, der den Index verwaltet. Die Tabelle wird nach diesem Index sortiert. Und den Index selbst, den muss ich nach Einfügen, Löschen und vor allem Verschieben manuell aktualisieren? Ist das richtig?
    […]

    Ja, was aber recht einfach ist. Bei der Anlage eines neuen Eintrages erzeugst du eine Instanz der "Mitgliedschaftsentität" und schaust dir die vergebenen Indexe an. Bedenke: Die Mitgliedschaften liegen ja in einer Entität vom Typen Playlist. Du musst dir also nur diejenigen anschauen. In etwa so:

    Quellcode

    1. -(NSManagedObject*)addSong:(NSManagedObject*)song toPlaylist:(NSmanagedObject*)playlist
    2. {
    3. // Index ermitteln
    4. NSNumber* lastIndex = [playlist valueForKeyPath:@"memberships.@max.index"];
    5. NSUInteger newIndex = [lastIndex unsignedInteger]+1;
    6. // Membership-Instanz erzeugen
    7. NSManagedObject* membership = … // Neue Instanz erzeugen.
    8. [membership setValue:[NSNumber numberWithUnsignedInteger:newIndex] forKey:@"index];
    9. [membership setValue:song forKey:@"song"];
    10. // Zur Playlist hinzufügen
    11. [[playlist mutableSetValueForKey:@"memberships"] addObject:memberhsip];
    12. }
    Alles anzeigen


    Natürlich nur schematisch.
    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"?
  • Danke für die Hilfe!
    Langsam krieg' ich den Gedanken. In einem kurzen Testprojekt hat das Einfügen und das Sortieren mit dem Index mit eurer Hilfe geklappt. Und zwar habe ich dafür einen NSArrayController für die Playlist und einen für die Einträge/Memberships mit dem Index.

    Ich bin mir aber nicht sicher, wo im Code die Logik hinsoll.

    Amin Negm-Awad schrieb:


    Quellcode

    1. -(NSManagedObject*)addSong:(NSManagedObject*)song toPlaylist:(NSmanagedObject*)playlist
    2. {
    3. // Index ermitteln
    4. NSNumber* lastIndex = [playlist valueForKeyPath:@"memberships.@max.index"];
    5. NSUInteger newIndex = [lastIndex unsignedInteger]+1;
    6. // Membership-Instanz erzeugen
    7. NSManagedObject* membership = … // Neue Instanz erzeugen.
    8. [membership setValue:[NSNumber numberWithUnsignedInteger:newIndex] forKey:@"index];
    9. [membership setValue:song forKey:@"song"];
    10. // Zur Playlist hinzufügen
    11. [[playlist mutableSetValueForKey:@"memberships"] addObject:memberhsip];
    12. }
    Alles anzeigen

    Wo soll diese Methode hin und von wem wird sie aufgerufen? Wo werden der Song und die Playlist, die als Parameter übergeben werden erzeugt (zum Testen hab ich Song und Membership erstmal selbst erzeugt)?

    Wo soll ich die Indizes der Einträge/Memberships aktualisieren? Brauche ich dafür eine eigene erbende Klasse vom NSArrayController?
  • Ok, mit der Hilfe habe ich alles super hinbekomme. Zwei Fragen hab' ich noch, dann gebe ich Ruhe.

    Eine Verständnisfrage:
    Der zweite NSArrayController(PlaylistEntry) ist bei mir ja an die Selektion des ersten (Playlist) gebunden. Wenn ich ihn nach den 'arrangedObjects' frage, liefert er mir eben die PlaylistEntries aus der Selektion zurück. Wo und wie aber stecken alle PlaylistEntries auf einmal in dem zweiten NSArrayController? Ich meine, wenn ich in der ersten selektieren Playlist 5 Songs habe, bekomme ich mit 'arrangedObjects' ein Set der Größe 5 vom zweiten ArrayController, wenn ich in der zweiten selektierten Playlist 7 Songs habe, bekomme ich ein Set der Größe 7. Wo, wie und in welcher Form aber stecken in dem ArrayController alle 12 drin?

    Die zweite Frage:
    Wie müsste ich jetzt ansetzen, wenn ich dem Nutzer erlauben wollte, eine beliebige Hierarchie von - sagen wir wieder Playlists und PlaylistEntries, obwohl es hier nicht gut passt - aufzubauen. Das heißt, er kann sich zur Laufzeit in jede Playlist, die er hat, nicht nur PlaylistEntries reinlegen, sondern weitere Playlists und in diese wieder beide Art von Entitäten usw. Wie könnte ich eure bestehenden Ansätze erweitern?

    Ich danke euch für eure Hilfe.
  • 1. Gar nicht. Dazu benötigst du einen weiteren Array-Controller, dessen Content ungebunden ist.

    2. Du meinst also eine Ordnerstruktur. Dafür eignen sich eigentlich Tree-Controller und Outline-View. Das System ist aber entsprechend.
    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"?
  • Sorry, dass ich noch einmal hier reinschreibe, aber ich wollte keinen neuen Thread aufmachen.

    Mittlerweile merke ich, dass meine PlaylistEntry- und Song-Instanzen beim Löschen zwar aus der GUI verschwinden, aber trotzdem in den Store gespeichert werden (sehe ich in der gespeicherten XML-Datei). Bei den Playlists selber funktioniert es wunderbar.

    Das Model sollte eigentlich so sein, wie von Markus Müller angegeben:

    Entity Playlist
    -->to many-relationship entries (optional; delete rule: Cascade)
    -->title

    Entity Entry
    --> to one relationship song (nicht optional; delete rule: Nullify)
    --> Integer index

    Entity Song
    -->String title;
    -->Data song;

    Ich habe einen NSArrayController für die Playlist. Playlists füge ich in eigener Methode zu, für's Entfernen bleibe ich bei 'remove:'.
    Auch der NSArrayController für die PlaylistEntries benutzt das normale 'remove:', aber die folgende Methode zum Hinzufügen (in MyDocument.m durch Knopfdruck ausgelöst):

    Quellcode

    1. - (IBAction)addPlaylistEntry:(id)sender;
    2. {
    3. // get managed object context
    4. NSManagedObjectContext *context = [self managedObjectContext];
    5. // get selected Playlist
    6. NSManagedObject *playlist = [playlistController selectedObjects] objectAtIndex:0];
    7. // get new index for PlaylistEntry
    8. NSNumber* lastIndex = [playlist valueForKeyPath:@"playlistEntries.@max.index"];
    9. int newIndex = [lastIndex intValue] + 1;
    10. // new PlaylistEntry with new index
    11. NSManagedObject *newEntry = [playlistEntriesController newObject];
    12. [playlistEntriesController addObject:newEntry];
    13. [newEntry setValue:[NSNumber numberWithInt:newIndex] forKey:@"index"];
    14. // new Song (den erstelle ich hier "manuell", da Songs nicht von einem NSArrayController verwaltet werden)
    15. NSManagedObjectModel *managedObjectModel = [[context persistentStoreCoordinator] managedObjectModel];
    16. NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Song"];
    17. NSManagedObject *song = [[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:context] autorelease];
    18. // the new Song is the content of the PlaylistEntry (to-one relationship)
    19. [newEntry setValue:song forKey:@"song"];
    20. // add PlaylistEntry to Playlist (to-many relationship)
    21. [[playlist mutableSetValueForKey:@"playlistEntries"] addObject:newEntry];
    22. // finally sort the PlaylistEntries according to CategoryEntry index
    23. NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES] autorelease];
    24. [playlistEntriesController setSortDescriptors:[NSArray arrayWithObject:sort]];
    25. }
    Alles anzeigen


    Es werden mit 'remove:' vom PlaylistEntry-ArrayController (auf Knopfdruck) weder die PlaylistEntries noch die Songs gelöscht (zwar aus der NSTableView, aber nicht aus dem Store). Playlists aber selbst werden mit 'remove:' vom Playlist-ArrayController ganz gelöscht. Statt mit KVC habe ich es auch mit den Core Data Generated Accessors versucht.
    (Mit NSManagedObjectContexts 'deleteObject:' verschwinden die Instanzen natürlich).

    Ich danke für jede Hilfe.
  • Und wieso verwendest du dann nicht deleteObject:?

    CD macht kein Reference-Counting. Da verschwndet nichts automatisch. Bei Bindings gibt es allerdings eine Option "delete on remove" oder so.

    Für das Löschen der Playlist kannst eine Löschweitergabe angeben: Delete-Rules. Aber das Verhalten ist doch richtig, oder? Bloß weil ein Song aus der Playlist verschwindet, heißt das ja nicht, dass er insgesamt weg ist.
    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"?
  • Vielen Dank für die schnelle Antwort!
    "Delete on remove" ist genau das, was ich möchte! Hat super geklappt!

    Ich hätte natürlich "deleteObject:" nehmen können, aber ich dachte, das ginge auch ohne eigene Verwaltung. Vor allem aber verwirrt es mich, wieso remove: vom Playlist-ArrayController die Playlists komplett löscht, während remove: vom PlaylistEntry-Controller (dessen Content Set an die Selektion des ersten gebunden ist) die PlaylistEntries nur aus der GUI verschwinden lässt.
  • Es lässt es nicht nur aus der GUI verschwinden. Es hat sogar mit der GUI nichts zu tun. Es verschwindet aus der Beziehung.

    Der Unterschied liegt darin, ob du den Array-Controller gebunden hast. Wenn er ungebunden ist, dann gibt es keine zwei Möglichkeiten: Immer delete. Ist er gebunden, dann hängt es eben von der Option ab.
    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"?
  • Grinch schrieb:

    Eine Verständnisfrage:
    Der zweite NSArrayController(PlaylistEntry) ist bei mir ja an die Selektion des ersten (Playlist) gebunden. Wenn ich ihn nach den 'arrangedObjects' frage, liefert er mir eben die PlaylistEntries aus der Selektion zurück. Wo und wie aber stecken alle PlaylistEntries auf einmal in dem zweiten NSArrayController? Ich meine, wenn ich in der ersten selektieren Playlist 5 Songs habe, bekomme ich mit 'arrangedObjects' ein Set der Größe 5 vom zweiten ArrayController, wenn ich in der zweiten selektierten Playlist 7 Songs habe, bekomme ich ein Set der Größe 7. Wo, wie und in welcher Form aber stecken in dem ArrayController alle 12 drin?

    Wenn Du pauschal alle haben möchtest, musst Du wie Amin schon gesagt hat einen separaten AC nehmen. Wenn Du multiple Selektion in Deiner Sourcelist haben möchtest (also mehrere Playlisten auf einmal auswählen möchtest), geht das auch über das Binding contentArrayForMultipleSelection Deines sourclist-AC. Da trägst Du selection.@distinctUnionOfArrays.entries ein.
    Hier gibts nähere Infos dazu.

    Gruß, Markus
  • Vielen Dank nochmal für eure Hilfe, dadurch bin ich echt weit gekommen!

    Um noch einmal auf die angesprochene Ordnerstruktur zurückzukommen, bei der ich mir gerade den Kopf zerbreche.
    Bislang habe ich das Model genau so, wie von Markus Müller angegeben. Außerdem habe ich der Playlist selber einen Index hinzugefügt, mit der ich diese sortieren kann.

    Jetzt möchte ich in Playlists andere Playlists und Songs reinlegen und das beliebig geschachtelt (ob das sinnvoll ist, ist erstmal egal).
    Wie muss ich dazu mein Model erweitern? Ich denke, ich muss den Index der Playlis ebenfalls in eine neue Entität auslagern (ähnlich wie bei den Songs)? Dann habe ich zwischen diesen beiden Identitäten eine neue Beziehung: der Wrapper hat eine To-One-Relation zu seinem Inhalt, der Playlist.
    Aber wie sieht die Beziehung von Ober-zu Unterplaylist aus? Ist dies ein To-Many-Relationship von der Playlist zum Wrapper (aber nicht das Inverse zu der eben genannten)? Oder ist dieser Ansatz falsch? Denn das erscheint mir zirkulär.

    Vielen Dank für die Hilfe.