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.
1 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { |
2 |
// Create Person
|
3 |
let entityPerson = NSEntityDescription.entityForName("Person", inManagedObjectContext: self.managedObjectContext) |
4 |
let newPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext) |
5 |
|
6 |
// Populate Person
|
7 |
newPerson.setValue("Bart", forKey: "first") |
8 |
newPerson.setValue("Jacobs", forKey: "last") |
9 |
newPerson.setValue(44, forKey: "age") |
10 |
|
11 |
return true |
12 |
}
|
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.
1 |
// Create Address
|
2 |
let entityAddress = NSEntityDescription.entityForName("Address", inManagedObjectContext: self.managedObjectContext) |
3 |
let newAddress = NSManagedObject(entity: entityAddress!, insertIntoManagedObjectContext: self.managedObjectContext) |
4 |
|
5 |
// Populate Address
|
6 |
newAddress.setValue("Main Street", forKey: "street") |
7 |
newAddress.setValue("Boston", forKey: "city") |
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.
1 |
// Add Address to Person
|
2 |
newPerson.setValue(NSSet(object: newAddress), forKey: "addresses") |
3 |
|
4 |
do { |
5 |
try newPerson.managedObjectContext?.save() |
6 |
} catch { |
7 |
let saveError = error as NSError |
8 |
print(saveError) |
9 |
}
|
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.
1 |
// Create Address
|
2 |
let otherAddress = NSManagedObject(entity: entityAddress!, insertIntoManagedObjectContext: self.managedObjectContext) |
3 |
|
4 |
// Set First and Last Name
|
5 |
otherAddress.setValue("5th Avenue", forKey:"street") |
6 |
otherAddress.setValue("New York", forKey:"city") |
7 |
|
8 |
// Add Address to Person
|
9 |
let addresses = newPerson.mutableSetValueForKey("addresses") |
10 |
addresses.addObject(otherAddress) |
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
.
1 |
// Delete Relationship
|
2 |
newPerson.setValue(nil, forKey:"addresses") |
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.



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.
1 |
// Create Another Person
|
2 |
let anotherPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext) |
3 |
|
4 |
// Set First and Last Name
|
5 |
anotherPerson.setValue("Jane", forKey: "first") |
6 |
anotherPerson.setValue("Doe", forKey: "last") |
7 |
anotherPerson.setValue(42, forKey: "age") |
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.
1 |
// Create Relationship
|
2 |
newPerson.setValue(anotherPerson, forKey: "spouse") |
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.



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.



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.
1 |
// Create a Child Person
|
2 |
let newChildPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext) |
3 |
|
4 |
// Set First and Last Name
|
5 |
newChildPerson.setValue("Jim", forKey: "first") |
6 |
newChildPerson.setValue("Doe", forKey: "last") |
7 |
newChildPerson.setValue(21, forKey: "age") |
8 |
|
9 |
// Create Relationship
|
10 |
let children = newPerson.mutableSetValueForKey("children") |
11 |
children.addObject(newChildPerson) |
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.
1 |
// Create Another Child Person
|
2 |
let anotherChildPerson = NSManagedObject(entity: entityPerson!, insertIntoManagedObjectContext: self.managedObjectContext) |
3 |
|
4 |
// Set First and Last Name
|
5 |
anotherChildPerson.setValue("Lucy", forKey: "first") |
6 |
anotherChildPerson.setValue("Doe", forKey: "last") |
7 |
anotherChildPerson.setValue(19, forKey: "age") |
8 |
|
9 |
// Create Relationship
|
10 |
anotherChildPerson.setValue(newPerson, forKey: "father") |
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.
1 |
// Create Fetch Request
|
2 |
let fetchRequest = NSFetchRequest(entityName: "Person") |
3 |
|
4 |
// Add Sort Descriptor
|
5 |
let sortDescriptor = NSSortDescriptor(key: "first", ascending: true) |
6 |
fetchRequest.sortDescriptors = [sortDescriptor] |
7 |
|
8 |
// Execute Fetch Request
|
9 |
do { |
10 |
let result = try self.managedObjectContext.executeFetchRequest(fetchRequest) |
11 |
|
12 |
for managedObject in result { |
13 |
if let first = managedObject.valueForKey("first"), last = managedObject.valueForKey("last") { |
14 |
print("\(first) \(last)") |
15 |
}
|
16 |
}
|
17 |
|
18 |
} catch { |
19 |
let fetchError = error as NSError |
20 |
print(fetchError) |
21 |
}
|
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.
1 |
Bart Jacobs |
2 |
Jane Doe |
3 |
Jim Doe |
4 |
Lucy Doe |
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.
1 |
// Add Sort Descriptor
|
2 |
let sortDescriptor1 = NSSortDescriptor(key: "last", ascending: true) |
3 |
let sortDescriptor2 = NSSortDescriptor(key: "age", ascending: true) |
4 |
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2] |
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.
1 |
Lucy Doe (19) |
2 |
Jim Doe (21) |
3 |
Jane Doe (42) |
4 |
Bart Jacobs (44) |
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.
1 |
// Fetching
|
2 |
let fetchRequest = NSFetchRequest(entityName: "Person") |
3 |
|
4 |
// Create Predicate
|
5 |
let predicate = NSPredicate(format: "%K == %@", "last", "Doe") |
6 |
fetchRequest.predicate = predicate |
7 |
|
8 |
// Add Sort Descriptor
|
9 |
let sortDescriptor1 = NSSortDescriptor(key: "last", ascending: true) |
10 |
let sortDescriptor2 = NSSortDescriptor(key: "age", ascending: true) |
11 |
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2] |
12 |
|
13 |
// Execute Fetch Request
|
14 |
do { |
15 |
let result = try self.managedObjectContext.executeFetchRequest(fetchRequest) |
16 |
|
17 |
for managedObject in result { |
18 |
if let first = managedObject.valueForKey("first"), last = managedObject.valueForKey("last"), age = managedObject.valueForKey("age") { |
19 |
print("\(first) \(last) (\(age))") |
20 |
}
|
21 |
}
|
22 |
|
23 |
} catch { |
24 |
let fetchError = error as NSError |
25 |
print(fetchError) |
26 |
}
|
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:
1 |
Lucy Doe (19) |
2 |
Jim Doe (21) |
3 |
Jane Doe (42) |
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.
1 |
let predicate = NSPredicate(format: "%K >= %i", "age", 30) |
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
.
1 |
let predicate = NSPredicate(format: "%K CONTAINS %@", "first", "j") |
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:
1 |
let predicate = NSPredicate(format: "%K CONTAINS[c] %@", "first", "j") |
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.
1 |
let predicate = NSPredicate(format: "%K CONTAINS[c] %@ AND %K < %i", "first", "j", "age", 30) |
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.
1 |
let predicate = NSPredicate(format: "%K == %@", "father.first", "Bart") |
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.