This has been asked before and people have given very good instructions on how to do this, e.g. here.
However, I was wondering if I really need to work with NSCoder if I simply wanted to save one NSMutableArray (containing various instances of another NSMutableArray) to a file? I tried this but only got an error message:
-(void)saveLibraryDat {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"myLibrary.dat"];
NSError *error;
[myLibrary writeToFile:filePath atomically:YES];
if (error) {
NSLog(@"There was an error saving myLibrary.dat: %@", error);
}
}
My error message:
2011-05-13 22:00:47.840 MoleNotes[15437:207] There was an error saving myLibrary.dat: (
1,
2
)
So I guess I have to work with NSCoder, right? If so, I was wondering how to go about this. People have explained how to do this with a class, but in my case, I have a NSMutableArray (myLibrary) which contains various instances of a class. Will I have to implement the NSCoder in this class and the NSMutableArray?
I alloc my library like this:
myLibrary = [[NSMutableArray alloc] init];
And then add instances of a class called NoteBook.m like this:
NoteBook *newNoteBook = [[NoteBook alloc] init];
newNoteBook.titleName = @"Notes"; // etc.
[myLibrary addObject:newNoteBook];
So where exactly do I put the NSCoder commands? Only into my NoteBook.m class? Will this automatically take care of myLibrary?
Thanks for any suggestions.
EDIT:
So I've updated my code, but I guess the big problem is that my NSMutableArray myLibrary contains several instances of a custom class I've set up (called notebook). I have set up NSCoding for this class (and all its variables) so that I can save it and load it.
Now my app works totally fine if I create the NSMutableArray in the app (i.e. when the app is started for the very first time, no file exists), instead of loading it from disk:
-(void) setupLibrary {
myLibrary = [[NSMutableArray alloc] init];
NoteBook *newNoteBook = [[NoteBook alloc] init];
newNoteBook.titleName = @"Notes";
/...
If I load it from disk, it works fine as well:
-(void)loadLibraryDat {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"myLibrary.dat"];
myLibrary = [[NSMutableArray alloc] init];
myLibrary = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
if (!myLibrary) {
// if it couldn't be loaded from disk create a new one
NSLog(@"myLibrary.dat empty... set up new one");
[self setupLibrary];
} else { NSLog(@"Loading myLibrary.dat successful."); }
}
If I log everything which is contained in my library after loading it, everything is still fine. E.g. the following works totally fine:
NSLog(@"%@", [[self.myLibrary objectAtIndex:0] titleName]);
The big problem is, however, if any other method tries to access myLibrary. For instance, if I call the very same log command from another method, the app will crash and I get this error message:
[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510
2011-05-14 14:09:10.490 Notes[17091:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510'
This sounds to me as if myLibrary has become deallocated somehow, but I can't see why. How could this have happened? I have the feeling that I did something wrong in my NSCoding set up... because if I simply create myLibrary in code, everything works like wonderfully. It's only if I load it from the disk, that the app will crash.
Here is the class setup:
#import <Foundation/Foundation.h>
@interface NoteBook : NSObject <NSCoding> {
NSString *titleName;
NSString *fileName;
NSMutableArray *tabTitles;
NSMutableArray *tabColours;
NSMutableArray *tabReference;
}
@property (nonatomic, retain) NSString *titleName;
@property (nonatomic, retain) NSString *fileName;
@property (nonatomic, retain) NSMutableArray *tabTitles;
@property (nonatomic, retain) NSMutableArray *tabColours;
@property (nonatomic, retain) NSMutableArray *tabReference;
-(id)initWithCoder:(NSCoder *)aDecoder;
-(void)encodeWithCoder:(NSCoder *)aCoder;
@end
//
// NoteBook.m
#import "NoteBook.h"
@implementation NoteBook
@synthesize titleName, fileName, tabTitles, tabColours, tabReference;
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.titleName = [aDecoder decodeObjectForKey:@"titleName"];
self.fileName = [aDecoder decodeObjectForKey:@"fileName"];
self.tabTitles = [aDecoder decodeObjectForKey:@"tabTitles"];
self.tabColours = [aDecoder decodeObjectForKey:@"tabColours"];
self.tabReference = [aDecoder decodeObjectForKey:@"tabReference"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:titleName forKey:@"titleName"];
[aCoder encodeObject:fileName forKey:@"fileName"];
[aCoder encodeObject:tabTitles forKey:@"tabTitles"];
[aCoder encodeObject:tabColours forKey:@"tabColours"];
[aCoder encodeObject:tabReference forKey:@"tabReference"];
}
@end
EDIT:
I think I've solved it... I forgot a little 'self'... which messed it all up and deallocated myLibrary:
self.myLibrary = [NSKeyedUnarchiver
unarchiveObjectWithFile:filePath];
if (self.myLibrary == nil) {
NSLog(@"myLibrary.dat empty... set up new one");
[self setupLibrary];
} else { NSLog(@"Loading myLibrary.dat successful."); }