Saving,Loading, and Multitasking

Every iOS application has its own application sandbox. An application sandbox is a directory on the filesystem that is barricaded from the rest of the filesystem. Your application must stay in its sandbox, and no other application can access your sandbox.

The application sandbox contains a number of directories:

application bundle This directory contains all the resources and the executable. It is read-only.
Library/Preferences/ where any Preferences/Settings are stored. handled automatically by NSUserDefaults and is backed up when the device is synchronized with iTunes.
tmp/ To get the path to the tmp directory in sandbox, you can use the fuction NSTemporaryDirectory.
Documents/ to persist between runs of application. It is backed up when the device is synchronized with iTunes.
Library/Caches/ unlike the Documents directory, it does not get backed up when the device is synchronized with iTunes.

Constructing a file path

To get the full path for a directory in the sandbox, you use the C function NSSearchPathForDirectoriesInDomains.

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *ourDocumentPath = [documentPaths objectAtIndex:0];

You can also get the path for the sandbox itself using the function NSHomeDirectory. Note that you cannot write files or create directories at the root-level of the sandbox. Any new directories or files must be created within one of the writeable directories in the sandbox: Documents, Library, or tmp. You can append the names of the directories to the string returned from this function.

NSString *sandboxPath = NSHomeDirectory();
// Once you have the full sandbox path, you can create a path from it
NSString *documentPath = [sandboxPath stringByAppendingPathComponent:@"Documents"];

However, it is safer to use NSSearchPathForDirectoriesInDomains than NSHomeDirectory with an appended directory name. The name of a directory could change in future releases of the operating system or you could mistype the string you are appending.

FileHelper.h

#import <Foundation/Foundation.h>
NSString *pathInDocumentDirectory(NSString *fileName);

FileHelper.m

#import "FileHelpers.h"
// To use this function, you pass it a file name, and it will construct
// the full path for that file in the Documents directory.
NSString *pathInDocumentDirectory(NSString *fileName)
{
  // Get list of document directories in sandbox
  NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
  // Get one and only document directory from that list
  NSString *documentDirectory = [documentDirectories objectAtIndex:0];
  // Append passed in file name to that directory, return it
  return [documentDirectory stringByAppendingPathComponent:fileName];
}

Usage

- (NSString *)possessionArchivePath
{
  // The returned path will be Sandbox/Documents/possessions.data
  // Both the saving and loading methods will call this method to get the same path,
  // preventing a typo in the path name of either method
  return pathInDocumentDirectory(@"possessions.data");
}

 Archiving objects

To write objects that conform to NSCoding to the filesystem, you use the class method archiveRootObject:toFile: of NSKeyedArchiver, which is a subclass of NSCoder. The first argument of this method is the root object, and the second argument is the path of the file to be written to.

In Possession.m , add the following implementation of encodeWithCoder:.

- (void)encodeWithCoder:(NSCoder *)encoder
{
  // For each instance variable, archive it under its variable name
  // These objects will also be sent encodeWithCoder:
  [encoder encodeObject:possessionName forKey:@"possessionName"];
  [encoder encodeObject:serialNumber forKey:@"serialNumber"];
  [encoder encodeObject:dateCreated forKey:@"dateCreated"];
  [encoder encodeObject:imageKey forKey:@"imageKey"];
  // For the primitive valueInDollars, make sure to use encodeInt:forKey:
  // the value in valueInDollars will be placed in the coder object
  [encoder encodeInt:valueInDollars forKey:@"valueInDollars"];
}

so to save,

- (BOOL)saveChanges
{
  // returns success or failure
  return [NSKeyedArchiver archiveRootObject:allPossessions
                                     toFile:[self possessionArchivePath]];
}

Unarchiving objects

 When an application wants to load archived objects, it unarchives them. First, an instance of NSCoder is created, and data from the filesystem is read into it. Then, the archived objects are decoded.

in Possession.m file

- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self) {
// For each instance variable that is archived, we decode it,
// and pass it to our setters. (Where it is retained)
[self setPossessionName:[decoder decodeObjectForKey:@"possessionName"]];
[self setSerialNumber:[decoder decodeObjectForKey:@"serialNumber"]];
[self setImageKey:[decoder decodeObjectForKey:@"imageKey"]];
// Make sure to use decodeIntForKey:, since valueInDollars is not an object
[self setValueInDollars:[decoder decodeIntForKey:@"valueInDollars"]];
// dateCreated is read only, we have no setter. We explicitly
// retain it and set our instance variable pointer to it
dateCreated = [[decoder decodeObjectForKey:@"dateCreated"] retain];
}
return self;
}

then

- (void)fetchPossessionsIfNecessary
{
// If we don't currently have an allPossessions array, try to read one from disk
if (!allPossessions) {
NSString *path = [self possessionArchivePath];
allPossessions = [[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain];
}
// If we tried to read one from disk but does not exist, then create a new one
if (!allPossessions) {
allPossessions = [[NSMutableArray alloc] init];
}
}

 Application States, Transitions, and Multitasking

- application:didFinishLaunchingWithOptions:
- applicationDidBecomeActive:
- applicationWillResignActive:
- applicationDidEnterBackground:
- applicationWillEnterForeground:

So, when should we save application data?

- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[PossessionStore defaultStore] saveChanges];
}

 _cmd

You already know about self, an implicit variable that points to the instance that is executing the current method.

There is another implicit variable called _cmd, which is the selector for the current method. You can get the NSString representation of a selector with the function NSStringFromSelector.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(@"%@", NSStringFromSelector(_cmd));
...
}

The Application Bundle

You can check out what an application bundle looks like on the filesystem after you install an application on the simulator.

Navigate to ~/Library/Application Support/iPhone Simulator/(version number)/Applications.

Right or Command-click the application bundle and chosse Show Package Contents from the contextual menu.

// Get a pointer to the application bundle
NSBundle *applicationBundle = [NSBundle mainBundle];
// Ask for the path to a resource named myImage.png in the bundle
NSString *path = [applicationBundle pathForResource:@"myImage"
                                             ofType:@"png"];

 

 

posted on 2012-06-17 10:45  grep  阅读(386)  评论(0编辑  收藏  举报