views:

1267

answers:

2

Hey guys,

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. What is the best way to accomplish this?

I've thought about using the 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!

A: 

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.

Brent Royal-Gordon
I won't be using subsets. I just need the full list persisted.How would one go about converting an object to be plist friendly? It was my understanding that there's a set of Property List Objects and that's that.My object isn't made of anything that isn't plistable...
Jasarien
+11  A: 

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.

Don McCaughey
And that would produce a nice plist? I was under the impression that writing out NSData would just produce serialized data and not a plist.
Jasarien
That produces an NSCoding archive—a file with preserved copies of your objects, kind of like a nib file (but you can't use Interface Builder to edit it). The "Archives and Serializations Programming Guide for Cocoa" in your documentation will explain it.
Brent Royal-Gordon
By default it archives to a binary plist, but you can change that to an xml plist if you want. Unfortunately it's not really human readable since it contains class and pointer metadata as well as your object data.
Don McCaughey
What to do if my class contains struct,enum etc ? I asked a question here. can u please answer. http://stackoverflow.com/questions/3492449/how-to-store-custom-objects-in-coredata
Sijo