Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persisting Custom Objects

I have a custom object that simply inherits from NSObject. It has 3 members - two floats and an NSDate.

My app is going to have an array with a number of these objects kicking around, and I need to persist it between runs. How can I accomplish this?

I've thought about using a SQLite db, but I'm thinking that it's a bit overkill since the only query I'd ever do would be select *.

In an ideal world I'd like to use an xml plist file. I'm not sure if I can do this with my custom object though. I know there's a set of Property List objects, and that NSArray comes under that, but writeToFile:atomically: only works with property list objects.

Any thoughts would be appreciated, thanks!

like image 202
Jasarien Avatar asked Mar 15 '09 17:03

Jasarien


1 Answers

NSCoding will do exactly what you want. I recommend you read up on it in the Apple docs, but I thought it was pretty straightforward to use. Your class (and any child classes) will need to implement the NSCoding protocol and you'll need to add -encodeWithCoder: and -initWithCoder: methods to your objects. Most of the common framework classes implement NSCoding already.

The code for your class will look something like this:

-(void) encodeWithCoder: (NSCoder*) coder {
  [coder encodeInteger: versionValue forKey: versionKey];
  [coder encodeObject: myStuff forKey: myStuffKey];
}

-(id) initWithCoder: (NSCoder*) coder {
  self = [super init];
  if ( ! self) return nil;
  myStuff = [[coder decodeObjectForKey: myStuffKey] retain];
  return self;
}

It's recommended you add a version number when encoding to give you flexibility to manage changes to your archive format in future versions.

In my class, I added a convenience method to archive my object:

-(void) archiveToFile: (NSString*) path {
  NSMutableData *data = [[NSMutableData alloc] init];
  NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: data];
  [archiver encodeObject: self forKey: myArchiveKey];
  [archiver finishEncoding];
  [archiver release];
  [data writeToFile: path atomically: YES];
  [data release];
}

and another one to unarchive or create a new object:

+(MyArchive*) newFromFile: (NSString*) path
            orWithMyStuff: (MyStuff*) myStuff
{
  NSData *data = [[NSData alloc] initWithContentsOfFile: path];
  NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
  MyArchive *myArchive = [unarchiver decodeObjectForKey: myArchiveKey];
  [unarchiver finishDecoding];

  if (myArchive) {
    [myArchive retain];
  } else {
    myArchive = [[MyArchive alloc] initWithStuff: myStuff;
  }
  [unarchiver release];
  [data release];
  return myArchive;
}

Since your top level object is an NSArray, you'll need to modify the last two methods for your case, but most of the boilerplate code will be the same.

like image 51
Don McCaughey Avatar answered Sep 28 '22 13:09

Don McCaughey