Hallo Zusammen,
fast jede App kommt mal zu dem Punkt an dem Daten gespeichert werden müssen. Sei es nun etwas triviales wir Highscores, komplette Spielstände oder Einstellungen der App (Sprache, Ton an/aus usw.)
Ein einfacher Weg dies zu tun ist die Benutzung von Singletons, welche ich hier schonmal vorgestellt habe, in Verbindung mit den NSUserDefaults. Wenn ihr noch kein Singleton Object habt, ist jetzt ein guter Zeitpunkt eins zu erstellen.
Die Idee dahinter ist recht einfach. Unser Singleton Object hält allle zu speichernden Werte. An bestimmten Stellen in der App, zum Beispiel, wenn der Home Button gedrückt wird, setzten wir einen Speicherbefehl an unser Singleton Object ab. Zu einem anderen Zeitpunkt, zum Beispiel wenn die App gestartet wird, erstellten wir unser Singleton Object neu und laden die Daten wieder ein.
Damit das alles funktioniert müssen wir unsere DataStore Klasse konform zum NSCoding Protokoll machen. Dazu genügt es im Header das Protkoll zu ergänzen. Zusätzlich fügen wir gleich die Instanzmethoden save und load ein.
@interface DataStore : NSObject <NSCoding>{ NSInteger optionsSound; } @property (readwrite) NSInteger optionsSound; + (DataStore *)getData; - (float)convertFontSizeToPad:(float)inputSize; - save; - load; @end
Laut der Apple Dokumentation zu NSCoding werden die Methoden encodeWithCoder und initWithCoder benötigt, um die Funktionen des Protokolls nutzen zu können.
-(void)encodeWithCoder:(NSCoder *)coder { [coder encodeInt:self.optionsSound forKey:@"Sound"]; }
-(id)initWithCoder:(NSCoder *)coder { if((self = [super init])) { self.optionsSound = [coder decodeIntForKey:@"Sound"]; } return self; }
In diesen beiden Methoden werden die Variabeln des Datastore zum speichern codiert und einem Schlüssel zugeordnet, bzw. an Hand des Schlüssels decodiert. Es gilt zu beachten, dass nur einfache Datentypen codiert und decodiert werden können. Möchtet ihr ganze Object wegspeichern, dann müssen auch diese wiederum das NSCoding Protokoll erfüllen und in einfache Datentypen zerlegt werden.
Wichtig ist auch stets die zur Variabel passende Methode zu nutzen.
Datentyp | encode Methode | decode Methode |
---|---|---|
Integer | encodeInt: forKey: | decodeIntForKey: |
String | encodeObject: forKey: | decodeObjectForKey: |
Bool | encodeBool: forKey: | decodeBoolForKey: |
Float | encodeFloat: forKey: | decodeFloatForKey: |
Jetzt ist unsere Klasse NSCoding konform. Allerdings werden die Daten bisher nur codiert und decodiert – um Speichern und Laden zu können brauchen wir zwei wetere Methoden. Wir schreiben daher nun die bereits definierten save und load Methoden.
-(void)save{ // save Data NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; DataStore *model = [DataStore getData]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model]; [defaults setObject:data forKey:@"DataStore"]; [defaults synchronize]; }
In Save erstellen wir uns zunächste ein NSUserDefaults Object und erstellen uns anschließend eine Referenz names model zum DataStore Singleton. Nun kommt die Codezeile, für das NSCoding Protokoll benötigt wird: wir codieren unser DataStore Object und wandeln es in NSData um. Durch [defaults setObject:data forKey:@“DataStore“]; befehligen wir das eigentliche abspeichern der Daten. Leider macht das iOS zu einem Zeitpunkt der ihm passt. Daher befehligen wir mit synchronize das sofortige speichern.
-(void)load{ // Load User Settings NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSData *data = [defaults objectForKey:@"DataStore"]; DataStore *gGameData = [NSKeyedUnarchiver unarchiveObjectWithData:data]; if(gGameData != NULL) { DataStore *model = [DataStore getData]; model.optionsSound = gGameData.optionsSound; } }
In der Load Methode gehen wir den umgekehrten Weg. Wir lesen ein NSData Object unter dem vorher zugewiesenen Schlüssel aus. Anschließend decodieren wir dieses NSData Object und weisen die Variabeln zu.
Wir können nun zu jeder beliebigen Zeit unsere Daten durch die Befehle
// speichern [[DataStore getData] save]; //laden [[DataStore getData] load];
speichern und laden. Die Aufrufe können prinzipiell überall im Quellcode erfolgen, sofern die DataStore Klasse importiert ist. Zum Speichern gibt es jedoch eine Stellen im AppDelegate, die sich ganz besonders anbieten: applicationWillResignActive wird immer dann aufgerufen, wenn die App unterbrochen wird.
-(void) applicationWillResignActive:(UIApplication *)application { if( [navController_ visibleViewController] == director_ ){ [director_ pause]; [[DataStore getData] save]; NSLog(@"RESIGN ACTIVE"); } }
Viel Spaß beim Speichern und bis zum nächsten mal.
Hey, I think your blog might be having browser compatibility issues.
When I look at your blog in Chrome, it looks fine but when opening in Internet Explorer,
it has some overlapping. I just wanted to give you a quick heads up!
Other then that, excellent blog!