iPhoneOS : Arkivera egna objekt

by trouf on 12/03/2010

Låt oss anta att vi vill designa en applikation som innehåller ett antal egendefinierade objekt.

Dessa behöver vi spara på något sätt, t.ex. när användaren stänger av applikationen eller om användaren skulle få ett telefonsamtal under tiden som applikationen körs.  Utifall att detta händer vill vi att applikationens variabler ska bevaras, och att även våra egendefinierade objekt ska sparas på samma sätt som andra inställningar sparas.

Att spara användardata är en återkommande punkt i de flesta iPhone-applikationer, och jag brukar använda NSUserDefaults för att på ett enkelt sätt spara och hämta data.

Data sparas på följande sätt


NSInteger intToSave = 23;

NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];

[preferences setInteger:intToSave forKey:@"keyToSavedInt"];

Och hämtas pÃ¥ följande sätt…


NSInteger intToRetrieve;

NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];

intToRetrieve = [preferences integerForKey:@"keyToSavedInt"];

I min mening det absolut smidigaste sättet att hantera användardata pÃ¥… det vill säga sÃ¥länge som man bara sparar Obj-C datatyper. Men om man däremot vill spara en array av egendefinierade typer, ja dÃ¥ blir det lite mindre smidigt.

Extra smidigt blir det om det dessutom är en hierarki av objekt.

Säg att vi har en applikation som ska hålla koll på lastbilar och deras last.

Låt oss använda en NSMutableArray för att hålla koll på alla lastbilar, och sedan definiera en klass för lastbilar, och en klass för last.

En näst intill pinsamt förenklad implementation av klassen Lastbil följer här.

Lastbil.h

#import <Foundation/Foundation.h>
#import "Last.h"

@interface Lastbil : NSObject {
	Last *last;
}

@property(nonatomic,retain) Last *last;

@end

Lastbil.m

#import "Lastbil.h"

@implementation Lastbil
     @synthesize last

-(id)init
{
    if (self = [super init])
    {
     //Init
		}

    return self;
}

- (void)dealloc
{
    [self.last release];
    [super dealloc];
}

@end

Och sÃ¥ lasten…

Last.h

#import <Foundation/Foundation.h>

@interface Last : NSObject {
	NSString *beskrivning;
}

@property(nonatomic,copy) NSString *beskrivning;

@end

Last.m

#import "Last.h"

@implementation Last

@synthesize beskrivning;

- (id)initWithBeskrivning: (NSString*)b
{
	self.beskrivning = b;
	return self;
}

- (void)dealloc
{
    [self.beskrivning release];
    [super dealloc];
}

@end

Ja nåt i den stilen. Blind kod och en knapphändig implementation, precis som det ska va.

Då är själva objekten deklarerade, det som återstår är att.. ja.. använda dem.

NSMutableArray *lastbilar = [[NSMutableArray alloc] init];

Lastbil *lastbil1 = [[Lastbil alloc] init];
Lastbil *lastbil2 = [[Lastbil alloc] init];

lastbil1.last = [[Last alloc] initWithBeskrivning:@"Fisk"];
lastbil2.last = [[Last alloc] initWithBeskrivning:@"Kött"];

[lastbilar addObject:lastbil1];
[lastbilar addObject:lastbil2];

[lastbilar release];

Då har vi en datastruktur som innehåller två lastbilar, den ena med fisk i lasten och den andra med gott, mört kött i lasten.

Om vi nu vill spara denna datastruktur till NSUserDefaults så skulle vi kunna göra det genom att först arkivera den till ett NSData-objekt med NSKeyedArchiver..

NSData *lastbilData = [NSKeyedArchiver archivedDataWithRootObject:lastbilar];

MEN detta är inte rumba, och kommer inte att fungera då våra egendefinierade objekt (Lastbil, Last) inte implementerar NSCoding protokollet. Alla objekt måste, om de ska kunna arkiveras, implementera metoder för hur de ska arkiveras och hur de ska avarkiveras. Enligt NSCoding måste alltså våra objekt implementera följande metoder.

- (void)encodeWithCoder:(NSCoder *)encoder
- (id)initWithCoder:(NSCoder *)decoder

Så låt oss implementera dessa metoder först i Lastbil.m. Det vi vill göra är att specifiera vilka av objektets egenskaper som ska arkiveras, och under vilka nycklas värdena ska arkiveras, detsamma för avarkivering.

- (void)encodeWithCoder:(NSCoder *)coder;
{
	[coder encodeObject:self.last forKey:@"last"];

}

- (id)initWithCoder:(NSCoder *)coder;
{
     if (self = [super init])
    {
		self.last = [coder decodeObjectForKey:@"last"];

	}
    return self;
}

…och sedan i Last.m

- (void)encodeWithCoder:(NSCoder *)coder;
{
	[coder encodeObject:self.beskrivning forKey:@"beskrivning"];

}

- (id)initWithCoder:(NSCoder *)coder;
{
     if (self = [super init])
    {
		self.beskrivning = [coder decodeObjectForKey:@"beskrivning"];

	}
    return self;
}

That’s pretty much it.

Nu går det bra att arkivera hela objekthierarkin, eftersom alla objekt i hierarkin kan arkiveras (NSCoder).

NSData *lastbilData = [NSKeyedArchiver archivedDataWithRootObject:lastbilar];

Och ett objekt av typen NSData går givetvis att spara till NSUserDefaults precis som motsvarande enkla datatyper.

NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];

NSData *lastbilData = [NSKeyedArchiver archivedDataWithRootObject:lastbilar];

[preferences setObject:lastbilData forKey:@"Lastbilar"];

Och går att läsa tillbaka, med minimal ansträngning.

NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];

NSData *lastbilData = [preferences objectForKey:@"Lastbilar"];

	if (lastbilData != nil)
	{
		NSArray *gamlaLastbilar = [NSKeyedUnarchiver unarchiveObjectWithData:lastbilData];

			if (gamlaLastbilar != nil)
			{
				//Presentera information
			}
	}

Och så gör man alltså för att arkivera sina egna objekt!

Andra bloggar om: , , ,

stefan.pataky@gmail.com

There is 1 comment in this article:

  1. 13/03/2010micke says:

    En lastbil kommer lastat…? 🙂

Write a comment: