Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Core Data

Core Data und Swift: Beziehungen und mehr Abruf

by
Read Time:12 minsLanguages:
This post is part of a series called Core Data and Swift.
Core Data and Swift: Managed Objects and Fetch Requests
Core Data and Swift: NSFetchedResultsController

German (Deutsch) translation by Federicco Ancie (you can also view the original English article)

Im vorherigen Artikel haben wir etwas über NSManagedObject erfahren und erfahren, wie einfach es ist, Datensätze mit Core Data zu erstellen, zu lesen, zu aktualisieren und zu löschen. Allerdings habe ich in dieser Diskussion keine Beziehungen erwähnt. Abgesehen von einigen Vorbehalten, die Sie beachten müssen, sind Beziehungen genauso einfach zu manipulieren wie Attribute. In diesem Artikel konzentrieren wir uns auf Beziehungen und werden auch unsere Erkundung von NSFetchRequest fortsetzen.

Voraussetzungen

Was ich in dieser Serie über Core Data beschreibe, gilt für iOS 7+ und OS X 10.10+, aber der Fokus wird auf iOS liegen. In dieser Serie werde ich mit Xcode 7.1 und Swift 2.1 arbeiten. Wenn Sie Objective-C bevorzugen, empfehle ich Ihnen, meine frühere Serie zum Core Data-Framework zu lesen.

1. Beziehungen

Wir haben bereits im Core Data-Modelleditor mit Beziehungen gearbeitet, und was ich Ihnen gleich erzähle, wird Ihnen daher bekannt vorkommen. Auf Beziehungen wird genau wie auf Attribute über die Schlüsselwertcodierung zugegriffen. Denken Sie daran, dass das zuvor in dieser Serie erstellte Datenmodell eine Person-Entität und eine Adress-Entität definiert. Eine Person ist mit einer oder mehreren Adressen verknüpft und eine Adresse ist mit einer oder mehreren Personen verknüpft. Dies ist eine Viele-zu-Viele-Beziehung.

Um die Adressen einer Person abzurufen, rufen wir einfach valueForKey(_:) für die Person auf, eine Instanz von NSManagedObject, und übergeben Adressen als Schlüssel. Beachten Sie, dass Adressen der Schlüssel sind, den wir im Datenmodell definiert haben. Welche Art von Objekt erwarten Sie? Die meisten Leute, die neu bei Core Data sind, erwarten ein sortiertes Array, aber Core Data gibt eine Menge zurück, die unsortiert ist. Das Arbeiten mit Sets hat seine Vorteile, wie Sie später erfahren werden.

Datensätze erstellen

Genug der Theorie, öffnen Sie das Projekt aus dem vorherigen Artikel oder klonen Sie es von GitHub. Beginnen wir damit, eine Person zu erstellen und sie dann mit einer Adresse zu verknüpfen. Um eine Person zu erstellen, öffnen Sie AppDelegate.swift und aktualisieren Sie application(_:didFinishLaunchingWithOptions:) wie unten gezeigt.

Dies sollte Ihnen bekannt vorkommen, wenn Sie den vorherigen Artikel gelesen haben. Das Erstellen einer Adresse sieht ähnlich aus, wie Sie unten sehen können.

Da jedes Attribut der Address-Entität als optional gekennzeichnet ist, müssen wir nicht jedem Attribut einen Wert zuweisen. Im Beispiel legen wir nur die Straßen- und Stadtattribute des Datensatzes fest.

Erstellen einer Beziehung

Um newAddress mit newPerson zu verknüpfen, rufen wir valueForKey(_:) auf und übergeben adresses als Schlüssel. Der von uns übergebene Wert ist eine NSSet-Instanz, die newAddress enthält. Sehen Sie sich zur Verdeutlichung den folgenden Codeblock an.

Wir rufen save() im Kontext des verwalteten Objekts von newPerson auf, um die Änderungen an den persistenten Speicher weiterzugeben. Denken Sie daran, dass der Aufruf von save() für einen Kontext eines verwalteten Objekts den Zustand des Kontexts eines verwalteten Objekts speichert. Das bedeutet, dass newAddress ebenso in den Backing Store geschrieben wird wie die gerade definierten Beziehungen.

Sie fragen sich vielleicht, warum wir newPerson nicht mit newAddress verknüpft haben, weil wir im Datenmodell eine inverse Beziehung definiert haben. Core Data schafft diese Beziehung für uns. Wenn eine Beziehung eine umgekehrte Beziehung hat, kümmert sich Core Data automatisch darum. Sie können dies überprüfen, indem Sie newAddress nach seinen persons fragen.

Abrufen und Aktualisieren einer Beziehung

Eine Beziehung zu aktualisieren ist auch nicht schwer. Die einzige Einschränkung besteht darin, dass wir Elemente aus der unveränderlichen NSSet-Instanz, die uns Core Data übergeben, hinzufügen oder entfernen müssen. Um diese Aufgabe zu vereinfachen, deklariert das NSKeyValueCoding-Protokoll jedoch eine praktische Methode mutableSetValueForKey(_:), die ein NSMutableSet-Objekt zurückgibt. Wir können dann einfach ein Element zur Sammlung hinzufügen oder daraus entfernen, um die Beziehung zu aktualisieren.

Schauen Sie sich den folgenden Codeblock an, in dem wir eine weitere Adresse erstellen und mit newPerson verknüpfen. Dazu rufen wir mutableSetValueForKey(_:) auf newPerson auf und fügen der veränderlichen Menge otherAddress hinzu. Es ist nicht erforderlich, Core Data mitzuteilen, dass wir die Beziehung aktualisiert haben. Core Data verfolgt das veränderliche Set, das es uns gegeben hat, und aktualisiert die Beziehung.

Löschen einer Beziehung

Sie können eine Beziehung löschen, indem Sie setValue(_:forKey:) aufrufen und nil als Wert und den Namen der Beziehung als Schlüssel übergeben. Im nächsten Code-Snippet trennen wir jede Adresse von newPerson.

2. Eins-zu-Eins- und Eins-zu-viele-Beziehungen

Eins-zu-eins-Beziehungen

Auch wenn unser Datenmodell keine Eins-zu-Eins-Beziehung definiert, haben Sie alles gelernt, was Sie wissen müssen, um mit dieser Art von Beziehung zu arbeiten. Das Arbeiten mit einer Eins-zu-Eins-Beziehung ist identisch mit dem Arbeiten mit Attributen. Der einzige Unterschied besteht darin, dass der Wert, den Sie von valueForKey(_:) zurückerhalten, und der Wert, den Sie an setValue(_:forKey:) übergeben, eine NSManagedObject-Instanz ist.

Lassen Sie uns das Datenmodell aktualisieren, um dies zu veranschaulichen. Öffnen Sie Core_Data.xcdatamodeld und wählen Sie die Entität Person aus. Erstellen Sie eine neue Beziehung und nennen Sie sie Ehepartner. Legen Sie die Entität Person als Ziel fest und legen Sie die Ehepartnerbeziehung als umgekehrte Beziehung fest.

Add a One-To-One Relationship to PersonAdd a One-To-One Relationship to PersonAdd a One-To-One Relationship to Person

Wie Sie sehen, ist es möglich, eine Beziehung zu erstellen, bei der das Ziel der Beziehung dieselbe Entität ist wie die Entität, die die Beziehung definiert. Beachten Sie auch, dass wir immer die Umkehrung der Beziehung festlegen. Wie in der Dokumentation von Apple angegeben, gibt es nur sehr wenige Situationen, in denen Sie eine Beziehung erstellen möchten, die keine inverse Beziehung hat.

Wissen Sie, was passiert, wenn Sie die Anwendung erstellen und ausführen? Das ist richtig, die Anwendung würde abstürzen. Da wir das Datenmodell geändert haben, ist der vorhandene Sicherungsspeicher, in diesem Beispiel eine SQLite-Datenbank, nicht mehr mit dem Datenmodell kompatibel. Um dies zu beheben, entfernen Sie die Anwendung von Ihrem Gerät oder dem Simulator und führen Sie die Anwendung aus. Keine Sorge, wir werden dieses Problem in einer zukünftigen Folge mithilfe von Migrationen eleganter lösen.

Wenn Sie die Anwendung ohne Probleme ausführen können, ist es Zeit für den nächsten Schritt. Gehen Sie zurück zur Anwendungsdelegatenklasse und fügen Sie den folgenden Codeblock hinzu.

Um eine anotherPerson als Ehepartner von newPerson zu setzen, rufen wir setValue(_:forKey:) auf newPerson auf und übergeben eine anotherPerson und "spouse" als Argumente. Wir können das gleiche Ergebnis erzielen, indem wir setValue(_:forKey:) für eine anotherPerson aufrufen und als Argumente newPerson und "spouse" übergeben.

Eins-zu-viele-Beziehungen

Lassen Sie uns mit einem Blick auf Eins-zu-Viele-Beziehungen abschließen. Öffnen Sie Core_Data.xcdatamodeld, wählen Sie die Person-Entität aus, und erstellen Sie eine Beziehung namens children. Setzen Sie das Ziel auf Person, setzen Sie den Typ auf To Many, und lassen Sie die inverse Beziehung vorerst leer.

Add a One-To-Many Relationship to PersonAdd a One-To-Many Relationship to PersonAdd a One-To-Many Relationship to Person

Erstellen Sie eine weitere Beziehung mit dem Namen Vater, legen Sie das Ziel auf Person und die umgekehrte Beziehung auf Kinder fest. Dadurch wird automatisch die umgekehrte Beziehung der untergeordneten Beziehung ausgefüllt, die wir vorhin leer gelassen haben. Wir haben jetzt eine Eins-zu-Viele-Beziehung aufgebaut, das heißt, ein Vater kann viele Kinder haben, aber ein Kind kann nur einen biologischen Vater haben.

Add a One-To-Many Relationship to PersonAdd a One-To-Many Relationship to PersonAdd a One-To-Many Relationship to Person

Kehren Sie zum Anwendungsdelegaten zurück und fügen Sie den folgenden Codeblock hinzu. Wir erstellen einen weiteren Personendatensatz, legen seine Attribute fest und legen ihn als untergeordnetes Element von newPerson fest, indem wir Core Data nach einem änderbaren Satz für die Schlüsselkinder fragen und den neuen Datensatz dem änderbaren Satz hinzufügen.

Der folgende Codeblock erreicht das gleiche Ergebnis, indem das father-Attribut einer anotherChildPerson festgelegt wird. Das Ergebnis ist, dass newPerson der Vater einer anotherChildPerson wird und eine anotherChildPerson ein Kind von newPerson wird.

3. Mehr Abruf

Das Datenmodell unserer Beispielanwendung ist an Komplexität stark gewachsen. Wir haben Eins-zu-Eins-, Eins-zu-Viele- und Viele-zu-Viele-Beziehungen geschaffen. Wir haben gesehen, wie einfach es ist, Datensätze, einschließlich Beziehungen, zu erstellen. Wenn wir diese Daten auch aus dem persistenten Speicher abrufen möchten, müssen wir mehr über das Abrufen erfahren. Beginnen wir mit einem einfachen Beispiel, in dem wir sehen, wie die von einer Abrufanforderung zurückgegebenen Ergebnisse sortiert werden.

Deskriptoren sortieren

Um die Datensätze zu sortieren, die wir aus dem Kontext des verwalteten Objekts zurückerhalten, verwenden wir die Klasse NSSortDescriptor. Sehen Sie sich das folgende Code-Snippet an.

Wir initialisieren eine Abrufanforderung, indem wir die Entität, an der wir interessiert sind, Person, übergeben. Wir erstellen dann ein NSSortDescriptor-Objekt, indem wir init(key:ascending:) aufrufen, zuerst das Attribut der Entität übergeben, nach der wir sortieren möchten, und einen booleschen Wert, der angibt, ob die Datensätze in aufsteigender oder absteigender Reihenfolge sortiert werden müssen.

Wir binden den Sortierdeskriptor an die Abrufanforderung, indem wir die sortDescriptors-Eigenschaft der Abrufanforderung festlegen. Da die Eigenschaft sortDescriptors vom Typ [NSSortDescriptor]? ist, ist es möglich, mehr als einen Sortierdeskriptor anzugeben. Wir werden uns diese Option gleich ansehen.

Der Rest des Codeblocks sollte Ihnen bekannt vorkommen. Die Abrufanforderung wird an den Kontext des verwalteten Objekts übergeben, der die Abrufanforderung ausführt, wenn wir executeFetchRequest(_:) aufrufen. Denken Sie daran, dass letztere eine Wurfmethode ist, was bedeutet, dass wir das Schlüsselwort try verwenden und die fetch-Anfrage in einer do-catch-Anweisung ausführen.

Führen Sie die Anwendung aus und überprüfen Sie die Ausgabe in der Xcode-Konsole. Die Ausgabe sollte ähnlich aussehen wie unten gezeigt. Wie Sie sehen, sind die Datensätze nach ihrem Vornamen sortiert.

Wenn Sie Duplikate in der Ausgabe sehen, stellen Sie sicher, dass Sie den Code auskommentieren, den wir zuvor geschrieben haben, um die Datensätze zu erstellen. Jedes Mal, wenn Sie die Anwendung ausführen, werden dieselben Datensätze erstellt, was zu doppelten Datensätzen führt.

Wie bereits erwähnt, ist es möglich, mehrere Sortierdeskriptoren zu kombinieren. Sortieren wir die Datensätze nach Nachname und Alter. Wir setzen zuerst den Schlüssel des ersten Sortierdeskriptors auf den letzten. Wir erstellen dann einen weiteren Sortierdeskriptor mit einem Altersschlüssel und fügen ihn dem Array der Sortierdeskriptoren hinzu.

Die Ausgabe zeigt, dass die Reihenfolge der Sortierdeskriptoren im Array wichtig ist. Die Datensätze werden zuerst nach ihrem Nachnamen und dann nach ihrem Alter sortiert.

Prädikate

Sortierdeskriptoren sind großartig und einfach zu verwenden, aber Prädikate machen das Abrufen in Core Data wirklich mächtig. Sortierdeskriptoren teilen Core Data mit, wie die Datensätze sortiert werden müssen. Prädikate teilen Core Data mit, an welchen Datensätzen Sie interessiert sind. Die Klasse, mit der wir arbeiten, ist NSPredicate.

Beginnen wir damit, jedes Mitglied der Doe-Familie zu holen. Dies ist sehr einfach und die Syntax wird einige von Ihnen an SQL erinnern.

Wir haben nicht viel geändert, abgesehen davon, dass wir ein NSPredicate-Objekt erstellen, indem wir init(format:arguments:) aufrufen und das Prädikat an die Abrufanforderung binden, indem wir dessen predicate-Eigenschaft festlegen. Beachten Sie, dass die Methode init(format:arguments:) eine variable Anzahl von Argumenten akzeptiert.

Die Formatzeichenfolge des Prädikats verwendet %K für den Eigenschaftsnamen und %@ für den Wert. Wie im Predicate Programming Guide angegeben, ist %K eine variable Argumentsubstitution für einen Schlüsselpfad, während %@ eine variable Argumentsubstitution für einen Objektwert ist. Dies bedeutet, dass der Formatstring des Prädikats unseres Beispiels last == "Doe" ausgewertet wird.

Wenn Sie die Anwendung ausführen und die Ausgabe in der Xcode-Konsole überprüfen, sollten Sie das folgende Ergebnis sehen:

Es gibt viele Operatoren, die wir zum Vergleich verwenden können. Neben = und ==, die hinsichtlich der Kerndaten identisch sind, gibt es noch >= und =>, <= und =>, != und <> sowie > und <. Ich empfehle Ihnen, mit diesen Operatoren zu experimentieren, um zu erfahren, wie sie sich auf die Ergebnisse der Abrufanforderung auswirken.

Das folgende Prädikat veranschaulicht, wie wir den Operator >= verwenden können, um nur Personendatensätze mit einem Altersattribut größer als 30 abzurufen.

Wir haben auch Operatoren für den Stringvergleich, CONTAINS, LIKE, MATCHES, BEGINSWITH und ENDSWITH. Lassen Sie uns jeden Personendatensatz abrufen, dessen Name den Buchstaben j CONTAINS.

Wenn Sie die Anwendung ausführen, ist das Ergebnisarray leer, da beim Zeichenfolgenvergleich standardmäßig die Groß-/Kleinschreibung beachtet wird. Wir können dies ändern, indem wir einen Modifikator wie folgt hinzufügen:

Sie können auch zusammengesetzte Prädikate mit den Schlüsselwörtern AND, OR und NOT erstellen. Im folgenden Beispiel holen wir jede Person ab, deren Vorname den Buchstaben j enthält und jünger als 30 ist.

Prädikate machen es auch sehr einfach, Datensätze basierend auf ihrer Beziehung abzurufen. Im folgenden Beispiel rufen wir jede Person ab, deren Vatername gleich Bart ist.

Das obige Prädikat funktioniert wie erwartet, da %K eine variable Argumentsubstitution für einen Schlüsselpfad ist, nicht nur ein Schlüssel.

Sie müssen sich daran erinnern, dass Sie mit Prädikaten den Hintergrundspeicher abfragen können, ohne dass Sie etwas über den Speicher wissen. Auch wenn die Syntax der Formatzeichenfolge des Prädikats in gewisser Weise an SQL erinnert, spielt es keine Rolle, ob es sich bei dem Sicherungsspeicher um eine SQLite-Datenbank oder einen Speicher im Arbeitsspeicher handelt. Dies ist ein sehr leistungsfähiges Konzept, das nicht nur auf Core Data beschränkt ist. Der Active Record von Rails ist ein weiteres gutes Beispiel für dieses Paradigma.

Prädikate enthalten viel mehr als das, was ich Ihnen in diesem Artikel gezeigt habe. Wenn Sie mehr über Prädikate erfahren möchten, empfehlen wir Ihnen, einen Blick auf das Predicate Programming Guide von Apple zu werfen. Wir werden in den nächsten Artikeln dieser Serie auch mehr mit Prädikaten arbeiten.

Abschluss

Sie haben jetzt ein gutes Verständnis der Grundlagen von Core Data und es ist an der Zeit, mit dem Framework zu arbeiten, indem Sie eine Anwendung erstellen, die die Leistungsfähigkeit von Core Data nutzt. Im nächsten Artikel lernen wir eine weitere wichtige Klasse des Core Data-Frameworks NSFetchedResultsController kennen. Dieser Kurs hilft uns, eine Sammlung von Datensätzen zu verwalten, aber Sie werden lernen, dass er noch viel mehr kann.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.