Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Geleitwort
Vorwort
1 Hello iPhone
2 Grundlagen
3 Views und Viewcontroller
4 Alles unter Kontrolle
5 Daten, Tabellen und Controller
6 Models, Layer, Animationen
7 Programmieren, aber sicher
8 Datenserialisierung und Internetzugriff
9 Jahrmarkt der Nützlichkeiten
A Sicherer Entwicklungszyklus
Stichwort

Download:
- ZIP, ca. 49,9 MB
Buch bestellen
Ihre Meinung?

Spacer
Apps programmieren für iPhone und iPad von Klaus M. Rodewig, Clemens Wagner
Das umfassende Handbuch
Buch: Apps programmieren für iPhone und iPad

Apps programmieren für iPhone und iPad
Galileo Computing
1000 S., geb., mit DVD
39,90 Euro, ISBN 978-3-8362-1915-0
Pfeil 5 Daten, Tabellen und Controller
Pfeil 5.1 Benachrichtigungen
Pfeil 5.1.1 Benachrichtigungen empfangen
Pfeil 5.1.2 Eigene Benachrichtigungen verschicken
Pfeil 5.2 Layoutanpassungen und Viewrotationen
Pfeil 5.2.1 Flexible Views dank Autosizing
Pfeil 5.2.2 Lang lebe das Rotationsprinzip!
Pfeil 5.2.3 Anpassung des Layouts
Pfeil 5.3 Autolayout
Pfeil 5.3.1 Restriktionen im Interface Builder festlegen
Pfeil 5.3.2 Restriktionen per Programmcode erstellen
Pfeil 5.3.3 Autolayout und Lokalisierung
Pfeil 5.4 Core Data
Pfeil 5.4.1 Datenmodellierung
Pfeil 5.4.2 Implementierung von Entitätstypen
Pfeil 5.4.3 Einbindung von Core Data
Pfeil 5.4.4 Der Objektkontext
Pfeil 5.4.5 Die Nachrichten des Objektkontexts
Pfeil 5.4.6 Anlegen und Ändern von Entitäten in der Praxis
Pfeil 5.5 Texte, Bilder und Töne verwalten
Pfeil 5.5.1 Die Tastatur betritt die Bühne
Pfeil 5.5.2 Fotos aufnehmen
Pfeil 5.5.3 Töne aufnehmen und abspielen
Pfeil 5.6 Tableviews und Core Data
Pfeil 5.6.1 Tableviews
Pfeil 5.6.2 Tabellenzellen gestalten
Pfeil 5.6.3 Tableviews unter iOS 5
Pfeil 5.6.4 Buttons in Tabellenzellen
Pfeil 5.6.5 Zellen löschen
Pfeil 5.7 Core Data II: Die Rückkehr der Objekte
Pfeil 5.7.1 Prädikate
Pfeil 5.7.2 Aktualisierung des Tableviews
Pfeil 5.7.3 Das Delegate des Fetchedresultscontrollers
Pfeil 5.7.4 Tabelleneinträge suchen
Pfeil 5.8 Twitter
Pfeil 5.8.1 Der Twitter-Account
Pfeil 5.8.2 Hinzufügen des Twitter-Frameworks
Pfeil 5.8.3 Integration in das Fototagebuch
Pfeil 5.9 Collectionviews
Pfeil 5.9.1 Der Collectionviewcontroller
Pfeil 5.9.2 Gitterdarstellung
Pfeil 5.9.3 Zellen und die Datenquelle
Pfeil 5.9.4 Ergänzende Views
Pfeil 5.9.5 Freie Layouts
Pfeil 5.9.6 Freie Layouts und ergänzende Views
Pfeil 5.9.7 Zellen auswählen
Pfeil 5.9.8 Decorationviews

Galileo Computing - Zum Seitenanfang

5.4 Core DataZur nächsten Überschrift

Das Beispielprogramm dieses Kapitels besitzt im Gegensatz zum analogen Wecker eine Modellschicht mit eigenen Klassen. Sie stellen die Daten des Fototagebuchs dar. Die Applikation kann diese Daten indes nicht nur anzeigen und bearbeiten, sondern auch im nichtflüchtigen Speicher des Gerätes ablegen und von dort auch wieder laden. Das Fototagebuch verwendet für die Datenmodellierung und -speicherung das Apple-Framework Core Data.

Core Data ist eine Technologie, um persistente Objektgraphen in Applikationen zu implementieren. Ein Objektgraph besteht aus Objekten, die durch Relationships miteinander verbunden sind, und Persistenz ist die dauerhafte Speicherung von Daten. Jede Applikation lässt natürlich nur bestimmte Objektgraphen zu. Wie die zulässigen Graphen eines Programms aussehen, legt dabei das jeweilige Datenmodell fest.

Solche Datenmodelle lassen sich am einfachsten durch Graphen beschreiben. Vielleicht kennen Sie ja auch schon Entity-Relationship-Diagramme (ER-Diagramme) oder UML-Klassendiagramme, die Ähnliches leisten. Sie stellen die Klassen des Datenmodells als Kästen dar, die durch Linien miteinander verbunden sind. Dabei repräsentieren diese Linien die Relationships des Modells. In Xcode ist ein Datenmodelleditor integriert, der sowohl eine grafische als auch eine dialog-orientierte Erstellung und Bearbeitung von Datenmodellen erlaubt. Die grafische Darstellung orientiert sich dabei an ER-Diagrammen.


Galileo Computing - Zum Seitenanfang

5.4.1 DatenmodellierungZur nächsten ÜberschriftZur vorigen Überschrift

Im Beispielprojekt PhotoDiary finden Sie die Datei Model.xcdatamodeld, die das Datenmodell des Fototagebuchs enthält. Wenn Sie die Datei auswählen, können Sie es so sehen wie in Abbildung 5.20.

Abbildung

Abbildung 5.20 Das Datenmodell in der Dialogdarstellung

Unter Entities finden Sie die Entitätstypen des Datenmodells. Ein Entitätstyp – häufig fälschlicherweise auch nur »Entität« genannt – beschreibt eine Objektklasse in einem Datenmodell. Er enthält Attribute und ist durch Relationships mit anderen Entitätstypen verbunden. Außerdem enthält der Entitätstyp noch weitere Informationen, wie beispielsweise die Objective-C-Klasse, die diesen Typ im Programm darstellt. Im Fototagebuch ist jedem Entitätstyp eine gleichnamige Klasse zugeordnet. Das muss jedoch nicht immer so sein. Die Basisklasse dieser Klassen ist in der Regel die Klasse NSManagedObject, die als Implementierung beliebiger Entitätstypen indes vollkommen ausreicht. Die Unterscheidung zwischen Entitätstypen und Klassen ist allerdings in der Praxis sekundär. Sie können sich also unter einem Entitätstyp ruhig so etwas wie eine Klasse vorstellen, die allerdings keine eigenen Methoden besitzt.

Durch die Buttongruppe Editor Style unten rechts im Xcode-Fenster können Sie auf die grafische Darstellung des Datenmodells umschalten. Die Diagrammdarstellung sehen Sie in Abbildung 5.21. Das Diagramm stellt Entitätstypen durch Kästen und Relationships durch Linien mit Pfeilenden dar. Die Kästen der Entitätstypen enthalten deren Namen als Titel und darunter die Namen der Attribute. Darunter sind die Namen der Relationships aufgeführt.

Das Datenmodell enthält zwei Entitätstypen: DiaryEntry und Medium. Während der erste Typ Tagebucheinträge beschreibt, dient der zweite zur Ablage von Mediendaten. In dem Tagebuch sind das Bilder und Tonaufnahmen. Über den Button Add Entity können Sie dem Modell weitere Entitätstypen hinzufügen. Über den Datenmodellinspektor (alt + cmd + 3) können Sie den Klassennamen des Entitätstyps festlegen, und über das Dropdown-Menü Parent Entity legen Sie den Obertyp eines Entitätstyps fest (siehe Abbildung 5.21). Sie haben also in einem Datenmodell auch die Möglichkeit der Vererbung.

Abbildung

Abbildung 5.21 Diagrammdarstellung des Datenmodells

Im Modell des Fototagebuchs verbinden zwei Relationships die beiden Entitätstypen miteinander, die in Abbildung 5.21 jedoch von nur einer Verbindungslinie dargestellt werden. Das liegt daran, dass die beiden Relationships zueinander invers sind. In Core Data sollte jede Relationship auch immer ein inverses Pendant haben. Neue Relationships legen Sie in der Diagrammdarstellung analog zu Outlets im Interface Builder an, indem Sie bei gedrückter rechter Maustaste eine Verbindung zwischen den beteiligten Entitätstypen ziehen. Wenn Sie eine Relationship durch das Ziehen einer Verbindung erzeugen, legt der Modelleditor automatisch die inverse Relationship an.

Natürlich enthalten beide Entitätstypen auch noch einige Attribute, deren Datentypen Sie in der Dialogansicht direkt festlegen können. Weitere Einstellungsmöglichkeiten zu jedem Attribut finden Sie im Datenmodellinspektor. Dazu wählen Sie das Attribut aus, drücken alt + cmd + 3 oder öffnen die Inspektorspalte auf der rechten Seite von Xcode und wählen dort das Icon ganz rechts aus (siehe Abbildung 5.22).

Bei allen Attributen können Sie drei boolesche Eigenschaften setzen:

  • Ein transientes Attribut wird nicht persistent gespeichert. Es geht also spätestens nach dem Programmende verloren.
  • Ein optionales Attribut darf auch den Wert nil annehmen. Wenn Sie hingegen versuchen, ein nicht optionales Attribut mit dem Wert nil zu speichern, liefert Core Data einen Fehler.
  • Für indizierte Attribute legt Core Data einen Index an. Er kann Suchanfragen beschleunigen, die nach diesem Attribut filtern.

Neben der Festlegung des Datentyps erlaubt der Inspektor auch noch datentypabhängige Einstellungen für einen Standardwert und Werteinschränkungen. Bei Strings können Sie beispielsweise eine minimale und maximale Länge sowie einen regulären Ausdruck festlegen, auf den die Werte passen müssen.

Abbildung

Abbildung 5.22 Der Datenmodellinspektor für ein Stringattribut

Der Entitätstyp verweist über die One-To-Many-Relationship media auf die zugehörigen Medien. Ein wichtiges Unterscheidungskriterium für Relationships sind die möglichen Anzahlen der Elemente an beiden Enden. Wenn die Relationship auf höchstens ein Element verweist, können Sie sie durch eine Property des Zieltyps darstellen. Ist hingegen mehr als ein Element möglich, müssen Sie dafür einen Sammlungstyp verwenden; in Core Data ist das NSSet. Bei der One-To-Many-Relationship media kann also immer ein Tagebucheintrag auf mehrere Medien verweisen.

Die Many-To-One-Relationship diaryEntry weist jedem Medium genau einen Tagebucheintrag zu. Sie ist die inverse Relationship zu media. Es gibt außerdem noch Many-To-Many-Relationships, die dieses Datenmodell allerdings nicht verwendet. Die Pfeilenden an der verknüpften Entität zeigen die Wertigkeit der Relationships an. To-One-Relationships haben eine einfache Pfeilspitze, und To-Many-Relationships haben eine doppelte Pfeilspitze. Sie können die Wertigkeit über den Datenmodellinspektor für die Relationship setzen (siehe Abbildung 5.23).

Bei Relationships können Sie neben dem Zielentitätstyp auch die inverse Relationship festlegen. Wie Attribute können Relationships ebenfalls transient und optional sein. Im Inspektor lässt sich allerdings nicht nur festlegen, ob es sich um eine To-One- oder To-Many-Relationship handelt, sondern auch, auf wie viele Elemente sie mindestens und höchstens verweisen darf. Die Delete Rule legt schließlich fest, was mit dem Objekt bei Löschung des Zielobjekts passiert. Es stehen vier Möglichkeiten zur Auswahl, wie Core Data bei einer Löschung des Ursprungsobjekts mit den Zielobjekten umgehen soll:

  • No Action: Die Zielobjekte werden nicht verändert.
  • Nullify: Core Data löscht den Verweis der inversen Relationship auf das Ursprungsobjekt. Core Data setzt also bei To-One-Beziehungen den Verweis auf nil, und bei To-Many-Beziehungen entfernt es das Ursprungsobjekt aus der inversen Relationship.
  • Cascade: Löscht auch alle Zielobjekte, auf die das Objekt verweist.
  • Deny: Core Data löscht das Objekt nur, wenn keine Zielobjekte existieren.

    Abbildung

    Abbildung 5.23 Der Datenmodellinspektor für Relationships

Die Relationship media verwendet kaskadierendes Löschen, weil die Medienobjekte ja zu dem jeweiligen Tagebucheintrag gehören. Wenn der Tagebucheintrag nicht mehr existiert, werden die Medien auch nicht mehr gebraucht. Das Löschen eines Tagebucheintrags bewirkt also automatisch auch das Löschen seiner Medien. Wenn Sie hingegen ein Medium löschen, soll es nur aus der Medienmenge des Tagebucheintrags entfernt werden. Der Tagebucheintrag bleibt allerdings bestehen.


Galileo Computing - Zum Seitenanfang

5.4.2 Implementierung von EntitätstypenZur nächsten ÜberschriftZur vorigen Überschrift

Das Fototagebuch stellt die Entitätstypen durch eigene Klassen dar, die Xcode direkt aus dem Datenmodell erzeugen kann. Wenn Sie diese Klassen anlegen möchten, wählen Sie im Dialog zum Anlegen einer neuen Datei das Template NSManagedObject subclass unter dem Punkt Core Data aus.

Tipp

Den Ablauf dieses Dialogs können Sie verkürzen, indem Sie vor dem Aufruf den zugrunde liegenden Entitätstyp auswählen. In diesem Fall brauchen Sie nämlich nur den Dateinamen festzulegen. Wenn Sie hingegen keinen Entitätstyp auswählen, können Sie die Klassen für mehrere Entitätstypen auf einmal erzeugen. Xcode zeigt Ihnen dazu entsprechende Auswahldialoge an.

Die erzeugte Klasse enthält für jedes Attribut des Entitätstyps eine Property-Deklaration, für die Sie in der Implementierung jedoch keine Synthesize-Anweisung finden. Stattdessen enthält die Datei für jede Property eine Dynamic-Anweisung:

@dynamic icon;
@dynamic creationTime;
@dynamic updateTime;
@dynamic text;
@dynamic media;

Listing 5.13 Property-Implementierung der Klasse »DiaryEntry«

Im Gegensatz zu den Synthesize-Anweisungen haben diese Anweisungen keine Auswirkung auf das erzeugte Programm. Sie zeigen dem Compiler lediglich an, dass der Implementierungsteil der Klasse für diese Propertys keine expliziten Methodendefinitionen enthält und Sie auch nicht die Synthesize-Anweisung vergessen haben. Stattdessen erfolgt der Zugriff auf die Attributwerte über Key-Value-Coding, wofür die Oberklasse NSManagedObject von DiaryEntry die entsprechenden Implementierungen besitzt.

Außerdem enthält die Headerdatei noch eine Kategorie mit vier Methoden, über die Sie die Objekte der Relationship media verändern können:

@interface DiaryEntry (CoreDataGeneratedAccessors)
- (void)addMediaObject:(Medium *)value;
- (void)removeMediaObject:(Medium *)value;
- (void)addMedia:(NSSet *)value;
- (void)removeMedia:(NSSet *)value;
@end

Listing 5.14 Kategorie mit Methoden

Auch diese Methoden implementiert Core Data über Key-Value-Coding. Da der Tagebucheintrag jedoch noch andere Operationen bei der Veränderung der Relationship ausführen soll, befindet sich diese Kategorie in der Implementierungsdatei der Klasse. Diese Methoden sind also privat.


Galileo Computing - Zum Seitenanfang

5.4.3 Einbindung von Core DataZur nächsten ÜberschriftZur vorigen Überschrift

Um einen Objektgraphen mit Core Data persistent speichern zu können, müssen Sie drei Objekte anlegen. Das Datenmodell repräsentiert ein Objekt der Klasse NSManagedObjectModel, das das Fototagebuch über einen Lazy-Getter des Application-Delegates aus dem Ressourcenordner der Applikation lädt:

- (NSManagedObjectModel *)managedObjectModel {Zeilenumbruch
if(managedObjectModel == nil) {Zeilenumbruch
NSURL *theURL = [[NSBundle mainBundle]Zeilenumbruch
URLForResource:@"Model" withExtension:@"momd"];

self.managedObjectModel = Zeilenumbruch
[[NSManagedObjectModel alloc] Zeilenumbruch
initWithContentsOfURL:theURL];
}
return managedObjectModel;
}

Listing 5.15 Laden des Datenmodells

Analog zu den XIB-Dateien übersetzt Xcode während des Buildprozesses die Modelldateien in ein anderes Format, das für die Ausführung günstiger ist. Aus diesem Grund hat die Ressource jetzt die Dateiendung .momd und nicht mehr .xcdatamodeld.

Das Application-Delegate erzeugt außerdem ein Objekt der Klasse NSPersistentStoreCoordinator über einen Lazy-Getter. Der Store-Koordinator verbindet die Entitätstypen eines Datenmodells mit einem Ablageort, den Unterklassen von NS-PersistentStore beschreiben. In iOS gibt es drei mögliche Arten von Stores: SQLite-Datenbanken, Binärdateien und die nichtpersistente Ablage im Hauptspeicher. Das Fototagebuch verwendet eine SQLite-Datenbank. Neben dem Typ müssen Sie außerdem eine URL auf eine Datei im lokalen Dateisystem angeben. Die App legt diese Datei Diary.sqlite in ihrem Dokumentenordner ab. Diesen Ordner können Sie so ermitteln, wie es in Listing 5.16 angegeben ist:

- (NSURL *)applicationDocumentsURL {
NSFileManager *theManager = Zeilenumbruch
[NSFileManager defaultManager];

return [[theManager URLsForDirectory:NSDocumentDirectoryZeilenumbruch
inDomains:NSUserDomainMask] lastObject];
}

Listing 5.16 Bestimmung des Dokumentenordners einer Applikation

Ein Store-Koordinator kann die Entitäten eines Datenmodells in unterschiedlichen Stores ablegen. Das können Sie über Konfigurationen festlegen. Das Fototagebuch macht davon allerdings keinen Gebrauch, sondern verwendet einen SQLite-Store. Es legt also die Entitäten in Tabellen einer SQL-Datenbank ab. Sie können einen neuen Store über die Methode addPersistentStoreWithType:configuration:URL:options:error: des Store-Koordinators anlegen. Sofern Sie Ihre Entitäten alle im gleichen Store ablegen möchten, dürfen Sie den Wert nil für den Konfigurationsparameter verwenden. Die URL gibt den Ablageort des Stores an. Momentan unterstützt Core Data nur Dateien im lokalen Dateisystem, weswegen Sie hier immer eine Datei-URL angeben müssen. Sie erkennen diese URLs an dem Protokoll beziehungsweise dem Präfix file.

- (NSPersistentStoreCoordinator *)storeCoordinator {
if(storeCoordinator == nil) {
NSURL *theURL = [[self applicationDocumentsURL] Zeilenumbruch
URLByAppendingPathComponent:@"Diary.sqlite"];
NSError *theError = nil;
NSPersistentStoreCoordinator *theCoordinator = Zeilenumbruch
[[NSPersistentStoreCoordinator alloc] Zeilenumbruch
initWithManagedObjectModel:
self.managedObjectModel];

if([theCoordinator
addPersistentStoreWithType:NSSQLiteStoreType Zeilenumbruch
configuration:nil URL:theURL options:nilð
error:&theError]) {
self.storeCoordinator = theCoordinator;
}
else {
NSLog(@"storeCoordinator: %@", theError);
}
}
return storeCoordinator;
}

Listing 5.17 Erzeugung des Store-Koordinators

Mit dem Store-Koordinator können Sie einen Objektkontext erstellen, über den Sie Ihre Objekte verwalten.


Galileo Computing - Zum Seitenanfang

5.4.4 Der ObjektkontextZur nächsten ÜberschriftZur vorigen Überschrift

Der Objektkontext ist eine Art Raum, in dem Ihre persistenten Objekte leben. Sie können in den Kontext Objekte aus der Datenhaltung hineinladen, neue Objekte erzeugen und enthaltene Objekte verändern oder löschen. Sie können die Änderungen schrittweise zurücknehmen (Undo) und auch die Rücknahme wieder zurücknehmen (Redo). Das alles hat allerdings zunächst keine Auswirkungen auf die Datenhaltung. Um die Änderungen Ihres Objektgraphen dauerhaft zu machen, müssen Sie den kompletten Objektkontext sichern. Der Objektkontext ist also ein »Schmierpapier«, auf dem Sie Ihren Objektgraphen verändern können. Wenn Ihnen das Ergebnis gefällt, machen Sie eine Reinzeichnung von dem Graphen, indem Sie ihn dauerhaft in der Datenhaltung speichern. Die Erzeugung eines Objektkontextes ist denkbar einfach. Sie erzeugen ein Objekt der Klasse NSManagedObjectContext und weisen diesem über die Property persistentStoreCoordinator einen Store-Koordinator zu.

Eine neue Entität – so heißen die Objekte eines Entitätstyps – erzeugen Sie hingegen nicht über eine alloc-init-Kette, sondern lassen sie über die Klassenmethode insertNewObjectForEntityForName:inManagedObjectContext: der Klasse NSEntityDescription anlegen. Der erste Parameter enthält dabei den Namen des gewünschten Entitätstyps und der zweite den Objektkontext, der das neue Objekt aufnimmt. Sie brauchen danach das Objekt also nicht mehr selber in einen Kontext einfügen. Jedes Objekt kann indes nur zu einem Kontext gehören.

Über die Methode deleteObject: können Sie ein Objekt aus einem Objektkontext löschen. Für die Aktualisierung von Objekten gibt es keine gesonderten Methoden im Objektkontext. Sie weisen den Propertys des Objekts einfach nur neue Werte zu. Der Objektkontext bekommt diese Änderungen automatisch mit.

Die Änderungen des Objektgraphen im Objektkontext können Sie dauerhaft durch einen Aufruf der Methode save: sichern. Das Fototagebuch schreibt also durch diesen Aufruf alle Änderungen in die SQLite-Datenbank. Der Parameter ist ein Zeiger auf eine Objektreferenz der Klasse NSError. Über diesen gibt die Methode eventuell aufgetretene Fehler beim Speichern zurück. Typische Fehler sind hierbei die Verletzung der Integritätsbedingungen des Entitätstyps. Wenn Sie beispielsweise versuchen, einen Tagebucheintrag ohne Erzeugungszeit zu speichern, liefert der Aufruf einen Fehler, da dieses Attribut obligatorisch ist.

Falls die genaue Fehlerursache Sie nicht interessiert, dürfen Sie auch NULL für diesen Parameter angeben. Sie können auch aus dem Rückgabewert des Methodenaufrufs den Erfolg ermitteln. Ein typischer Methodenaufruf mit Rückgabe des Fehlerobjekts sieht also so aus:

NSError *theError = nil;
if(![theContext save:&theError]) {
// Verarbeitung des Fehlers
}

Listing 5.18 Sichern eines Objektkontextes mit Fehlerermittlung

Zeiger auf Objektreferenzen

In Cocoa Touch gibt es viele Methoden, die wie die Methode save: einen Zeiger auf eine Objektreferenz übergeben bekommen. Diese Zeiger sind C-Pointer und keine Objektreferenzen, weswegen Sie dafür NULL und nicht nil verwenden sollten. Umgekehrt sollten Sie Objektreferenzen auch nicht auf NULL setzen. Diese Unterscheidung ist eine Konvention, und ihre Missachtung führt (derzeit) zu keinen Compiler- oder Laufzeitfehlern. Es zeugt jedoch von gutem Stil, wenn Sie sich an die Konvention halten.

Über die Methode reset können Sie alle Änderungen in einem Objektkontext rückgängig machen. Dabei werden alle Objekte aus dem Kontext entfernt. Der Kontext befindet sich also nach dem Aufruf in seinem ursprünglichen Zustand.


Galileo Computing - Zum Seitenanfang

5.4.5 Die Nachrichten des ObjektkontextsZur nächsten ÜberschriftZur vorigen Überschrift

Sie können sich über Änderungen Ihrer Objekte im Objektkontext informieren lassen. Da jede Änderung an den Modellobjekten mehrere Controller betreffen kann, sind Delegates für die Benachrichtigung indes ungeeignet. Stattdessen versendet der Objektkontext Benachrichtigungen, sodass er auch mehrere Controller auf einmal benachrichtigen kann. Es gibt drei Arten von Benachrichtigungen. Dabei ist das Objekt der Benachrichtigung immer der Objektkontext, in dem die App die Änderung durchgeführt hat, und das User-Info-Dictiontionary enthält die geänderten Objekte.

Der Objektkontext sendet eine Benachrichtigung vom Typ NSManagedObjectContextObjectsDidChangeNotification, wenn Sie die Attribute einer Entität im Kontext verändern. Unter dem Schlüssel NSUpdatedObjectsKey finden Sie die geänderten Objekte in der User-Info. Vor und nach dem Sichern versendet der Objektkontext jeweils eine Benachrichtigung vom Typ NSManagedObjectContextWillSaveNotification beziehungsweise NSManagedObjectContextDidSaveNotification. Die DidSave-Notification enthält im User-Info-Dictionary die geänderten Objekte unter den Schlüsseln NSUpdatedObjectsKey, NSInsertedObjectsKey und NSDeletedObjectsKey. Diese Benachrichtigungen erleichtern es Ihnen, die Anzeigen in Ihren Viewcontrollern aktuell zu halten. Sie können beispielsweise die Zellen in einem Tableview relativ einfach über diese Benachrichtigungen aktualisieren lassen. Wie das geht, beschreibt Abschnitt 5.6, »Tableviews und Core Data«.


Galileo Computing - Zum Seitenanfang

5.4.6 Anlegen und Ändern von Entitäten in der PraxisZur vorigen Überschrift

In dem Beispielprogramm Fototagebuch gibt es einen eigenen Viewcontroller für die Erzeugung und Bearbeitung eines Tagebucheintrags. Er hat die Klasse ItemViewController, die eine private Property managedObjectContext besitzt. Sie verweist auf ihren eigenen Objektkontext, den der Getter erzeugt und initialisiert (siehe Listing 5.19):

- (NSManagedObjectContext *)managedObjectContext {
if(managedObjectContext == nil) {
id theDelegate = Zeilenumbruch
[[UIApplication sharedApplication] delegate];

self.managedObjectContext = Zeilenumbruch
[[NSManagedObjectContext alloc] init];
self.managedObjectContext.persistentStoreCoordinator =Zeilenumbruch
[theDelegate storeCoordinator];
}
return managedObjectContext;
}

Listing 5.19 Verwaltung des Objektkontextes über einen Lazy-Getter

Abbildung

Abbildung 5.24 Anlegen eines neuen Eintrags

Ein Eintrag kann dabei jeweils einen Text, ein Bild und einen Ton enthalten. Außerdem enthält der Eintrag noch das Erzeugungs- und das Aktualisierungsdatum. Diese beiden Werte setzt der Eintrag automatisch vor dem Speichern. Der Controller verwaltet den Eintrag über zwei Propertys der Klasse DiaryEntry. Über die öffentliche Property diaryEntry können andere Klassen den Eintrag auslesen und schreiben, während die private, interne Property item dazu dient, den Eintrag zu halten. Während die Klasse die Property item über eine @synthesize-Anweisung implementiert, erfolgt die Implementierung von diaryEntry durch entsprechende Methoden, die auf der Verwendung von item basieren.

Bei der Anzeige des Controllers gibt es zwei Fälle. Entweder möchte der Nutzer einen bestehenden Eintrag verändern oder einen neuen anlegen. Vor der Anzeige setzt der Aufrufer dazu die Property diaryEntry entsprechend – entweder auf den zu verändernden Eintrag oder auf nil. Anhand dieses Wertes kann der Setter entscheiden, wie er den Objektkontext verändern muss.

Für den Wert nil muss er einen neuen Eintrag erzeugen und in den Objektkontext legen. Andernfalls muss er überprüfen, ob der Eintrag bereits in dem Objektkontext des Controllers liegt. Falls das nicht der Fall ist, muss er das Objekt in den Kontext laden. Dazu verwendet die Implementierung die Methode objectWithID: des Kontexts. Jede CoreData-Entität hat eine eindeutige Kennung. Sie können diese über die Methode objectID der Klasse NSManagedObject jeder Entität abfragen. Den auf diese Weise geladenen Eintrag müssen Sie natürlich der Property item zuweisen, da Sie ja mit ihm weiterarbeiten möchten. Den Code zur Vorbereitung des Objektkontextes finden Sie in Listing 5.20.

- (DiaryEntry *)diaryEntry {
return self.item;
}

- (void)setDiaryEntry:(DiaryEntry *)inDiaryEntry {
NSManagedObjectContext *theContext =Zeilenumbruch
self.managedObjectContext;

[theContext reset];
if(inDiaryEntry == nil) {
self.item = [NSEntityDescriptionZeilenumbruch
insertNewObjectForEntityForName:@"DiaryEntry"Zeilenumbruch
inManagedObjectContext:theContext];
}
else {
self.item = (DiaryEntry *)[theContextZeilenumbruch
objectWithID:inDiaryEntry.objectID];
}
[self applyItem];
}

Listing 5.20 Vorbereitung des Objektkontexts

Der Aufruf der Methode reset am Anfang entfernt eventuell noch vorhandene Objekte aus dem Kontext. Der Kontext soll ja schließlich nur den aktuellen Tagebucheintrag verwalten. Durch den Aufruf der Methode applyItem überträgt der Controller die Werte des gesetzten Tagebucheintrags auf die Viewelemente.

Das Speichern des Eintrags ist dagegen weniger aufwendig. Sie rufen dafür die Methode save: des Objektkontexts des Controllers auf. Das geschieht im Controller in der Methode saveItem, die die Methode viewWillDisapper: aufruft. Sie speichert allerdings keine leeren Einträge. Ob ein Eintrag einen Inhalt hat, stellt sie über die Methode hasContent des Tagebucheintrags fest.

- (BOOL)saveItem {
BOOL theResult = NO;

if(self.item.hasContent) {
NSError *theError = nil;

theResult =
[self.managedObjectContext save:&theError];
if(!theResult) {
NSLog(@"saveItem: %@", theError);
}
}
return theResult;
}

Listing 5.21 Speichern des Kontexts

Die Methode hasContent in der Klasse DiaryEntry überprüft dabei einfach die Länge des Textes und die Anzahl der angehängten Medien.

- (BOOL)hasContent {
return self.text.length > 0 || self.media.count > 0;
}

Listing 5.22 Inhaltsprüfung eines Tagebucheintrags

Der Controller kann jetzt den Eintrag verändern, indem er die Attribute des Eintrags setzt. Der Eintrag hat zwei Zeitstempel für den Erzeugungs- und den letzten Aktualisierungszeitpunkt des Eintrags. Diese Werte können Sie automatisch setzen, indem Sie in der Klasse DiaryEntry zwei Methoden der Klasse NSManagedObject überschreiben. Wenn Sie ein neues Objekt in einen Objektkontext einfügen, ruft Core Data die Methode awakeFromInsert des Objekts auf. Sie können diese Methode dazu verwenden, die Erzeugungs- und Aktualisierungszeit zu setzen.

- (void)awakeFromInsert {
[super awakeFromInsert];
NSDate *theDate = [NSDate date];
[self setPrimitiveValue:theDate forKey:@"creationTime"];
[self setPrimitiveValue:theDate forKey:@"updateTime"];
}

Listing 5.23 Vorbelegung der Erzeugungs- und Aktualisierungszeit

Die Aktualisierungszeit muss außerdem beim Sichern des Objekts aufgefrischt werden. Das können Sie über die Methode willSave bewerkstelligen. Der Name dieser Methode ist allerdings etwas missverständlich. Core Data ruft sie auf, bevor die Änderungen des Objekts in der Datenhaltung gesichert werden. Dazu zählt auch das Löschen der Entität, und Sie können diesen Sonderfall über die Methode isDeleted abfragen.

- (void)willSave {
[super willSave];
if(!self.isDeleted) {
NSDate *theDate = [NSDate date];
[self setPrimitiveValue:theDate forKey:@"updateTime"];
}
}

Listing 5.24 Erneuerung der Aktualisierungszeit beim Sichern

Die Methoden in Listing 5.23 und Listing 5.24 benutzen beide die Methode setPrimitiveValue:forKey: anstatt der Setter. Der Setter hat hier Nebeneffekte, die im Falle der Methode willSave zu einer Endlosrekursion führen. Durch die Verwendung von setPrimitiveValue:forKey: vermeiden Sie diese Nebeneffekte, da diese Methode nur den neuen Wert setzt.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.







<< zurück
  Zum Katalog
Zum Katalog: Apps programmieren für iPhone und iPad

Apps programmieren für iPhone und iPad
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Einstieg in Objective-C 2.0 und Cocoa





 Einstieg in
 Objective-C 2.0
 und Cocoa


Zum Katalog: Apps entwickeln für iPhone und iPad - Videotraining






 Apps entwickeln für
 iPhone und iPad -
 Videotraining


Zum Katalog: Apps mit HTML5 und CSS3






 Apps mit HTML5
 und CSS3


Zum Katalog: iPhone- und iPad-Apps entwickeln






 iPhone- und
 iPad-Apps entwickeln


Zum Katalog: Android 4






 Android 4


Zum Katalog: Android-Apps entwickeln - Videotraining






 Android-Apps
 entwickeln -
 Videotraining


Zum Katalog: Windows Store Apps mit XAML und C#






 Windows Store Apps
 mit XAML und C#


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2013
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de