Question

I'm trying to save for the first time. Made my application, and now going to save data and load on startup and close. Looked into NSKeyedArchiver and added the two methods from the NSCoding to all my custom classes:

-(void)encodeWithCoder:(NSCoder *)aCoder{
 // Encode Stuff
}

-(id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init])
        // Decode Stuff
    return self;
}

But now i'm finding a problem, where and how do i need to call my saveFile and loadFile? My data is stored in 2 arrays in my FirstViewController. And i wanted to save the data with the AppDelegate's - (void)applicationWillTerminate:(UIApplication *)application and load data with - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions But how is this possible to reach the FirstViewController class with the Arrays in it to store from the AppDelegate method's?

Is the thinking right, or do i need to do it on an other way?

Kind Regards

Was it helpful?

Solution

Loading data should be "lazy". This means the data should be loaded the first instant that you actually need to read the data. Also, if it's a lot of data, you should be prepared to free it while your app is running so other apps can use the RAM, this means your app is more likely to still be running next time the user launches your app.

So, make a class that provides access to the data, and the first time anything needs data it checks if the internal NSCoding object is nil, and if it is then that is where you should load the data.

As for saving, you should save before terminating but more importantly you should also save within a second or so of any data being modified by the user. Your app should crash at any moment due to a software bug, or it could be terminated for some other reason, or the battery could simply run out.

Lets say your internal data storage is an NSMutableDictionary saved using NSKeyedArchiver. It has a value with the key @"value", with a "getter" and "setter" implemented like this:

- (NSString *)value
{
  if (!self.data)
    self.data = [NSKeyedUnarchiver unarchiveObjectWithFile:self.dataFile];

  return self.data[@"value"];
}

- (void)setValue:(NSString *)value
{
  if (!self.data)
    self.data = [NSKeyedUnarchiver unarchiveObjectWithFile:self.dataFile];

  self.data[@"value"] = value;
  self.needsSave = YES;

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self save];
  });
}

- (void)save
{
  if (!self.needsSave)
    return;

  [NSKeyedArchiver archiveRootObject:self.data toFile:self.dataFile];
  self.needsSave = NO;
}

Finally, your class should also register for UIApplicationDidReceiveMemoryWarningNotification, UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, where you want to save to disk and free the RAM so other apps can use it:

- (id)init
{
  if (!(self = [super init]))
    return nil;

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveAndFreeMemory:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveAndFreeMemory:) name:UIApplicationWillResignActiveNotification object:nil];
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveAndFreeMemory:) name:UIApplicationWillTerminateNotification object:nil];

  return self;
}

- (void)saveAndFreeMemory:(NSNotification *)notif
{
  [self save];

  self.data = nil;
}

OTHER TIPS

When a UIApplication launches in the normal way, you can always call [[UIApplication sharedApplication] delegate] from any object in the app, and you will thus obtain a reference to the application's delegate.

This is how view controller code generally gets ahold of the app delegate, you establish the connection in the view controllers in their respective -awakeFromNib methods -- it's generally less gotcha-prone to have the view controllers make the first contact with the app delegate, than having the app delegate reach out to the view controllers.

Have your app delegate decode the saved data into a model object hierarchy, have your view controllers connect with the app delegate and begin KV observing the model in -awakeFromNib.

Or just use Core Data.

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