Question

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!

Was it helpful?

Solution

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.

OTHER TIPS

If you're going to want to retrieve a subset of the objects, SQLite is by far your best choice.

If not, it's a choice between plist format and NSCoding. plist requires you to be able to convert your custom objects into something plist-friendly and then convert the plist back into your objects, but NSCoding is a little harder to wrap your head around and can't be easily edited (or examined) by hand.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top