IT tutorials
 
Mobile
 

iPhone Application Development : Implementing File System Storage (part 4) - Implementing Object Archiving & Archiving in the Flash Cards

9/30/2011 11:29:25 AM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019

Implementing Object Archiving

A running iPhone application has a vast number of objects in memory. These objects are interlinked to one another with references in such a way that if you were to visualize the relationships, they would appear like a tangled spider web. This web of all the objects in an application and all the references between the objects is called an object graph.

A running iPhone application is not much more than the program itself (which is always the same, at least until the user installs an update) and the unique object graph that is the result of all the activity that has occurred in the running of the application up to that point. One approach to storing an application’s data (so that it is available when the application is launched again in the future) is to take the object graph, or a subset of it, and store the object graph on the file system. The next time the program runs, it can read the graph of objects from the file system back into memory and pick up where it left off, executing the same program with the same object graph.

Most object-oriented development environments have a serialization mechanism that is used to stream a graph of objects out of memory and onto a file system and then back into memory again at a later time. Object archiving is the Cocoa version of this process. There are two main parts to object archiving: NSCoder and NSCoding. An NSCoder object can archive (encode and decode) any object that conforms to the NSCoding protocol. Apple supplies NSCoder for most data types, and any custom objects we want to archive implement the NSCoding protocol. We are in luck because the NSCoding protocol consists of just two methods: initWithCoder and encodeWithCoder.

Let’s start with encodeWithCoder. The purpose of encodeWithCoder is to encode all the instance variables of an object that should be stored during archival. To implement encodeWithCoder, decide which instance variables will be encoded and which instance variables, if any, will be transient (not encoded). Each instance variable you encode must be a scalar type (a number) or must be an object that implements NSCoding. This means all the instance variables you’re likely to have in your custom objects can be encoded because the vast majority of Cocoa Touch and Core Foundation objects implement NSCoding. On the iPhone, NSCoder uses keyed encoding, so you provide a key for each instance variable you encode. The encoding for our FlashCard class is shown in Listing 14.

Listing 14.
- (void)encodeWithCoder:(NSCoder *)encoder {

[encoder encodeObject:self.question forKey:kQuestion];
[encoder encodeObject:self.answer forKey:kAnswer];
[encoder encodeInt:self.rightCount forKey:kRightCount];
[encoder encodeInt:self.wrongCount forKey:kWrongCount];

}

Notice that we used the encodeObject:forKey method for NSStrings and you’d use the same for any other objects. For integers, we used encodeInt:forKey. You can check the API reference documentation of NSCoder for the complete list, but a few others you should be familiar with are encodeBool:forKey and encodeDouble:forKey and encodeBytes:forKey. You’ll need these for dealing with Booleans, floating-point numbers, and data.

The opposite of encoding is decoding, and for that part of the protocol there is the initWithCoder method. Like encodeWithCoder, initWithCoder is keyed, but rather than providing NSCoder an instance variable for a key, you provide a key and are returned an instance variable. For our FlashCard class, decoding should be implemented, as in Listing 15.

Listing 15.
- (id)initWithCoder:(NSCoder *)decoder {

if (self = [super init]) {
self.question = [decoder decodeObjectForKey:kQuestion];
self.answer = [decoder decodeObjectForKey:kAnswer];
self.rightCount = [decoder decodeIntForKey:kRightCount];
self.wrongCount = [decoder decodeIntForKey:kWrongCount];
}
return self;

}

The four keys we used should be defined as constants in the FlashCard.h header file. Add them now:

#define kQuestion @"Question"
#define kAnswer @"Answer"
#define kRightCount @"RightCount"
#define kWrongCount @"WrongCount"

The last step is to update the class definition in the FlashCard.h header file to indicate that FlashCard implements the NSCoding protocol:

@interface FlashCard : NSObject <NSCoding> {

Our FlashCard class is now archivable, and an object graph that includes FlashCard object instances can be persisted to and from the file system using object archiving.

Archiving in the Flash Cards

To fix the fatal flaw in the FlashCards application, we need to store all the flash cards on the file system. Now, because FlashCard implements the NSCoding protocol, each individual flash card is archivable. Remember that object archiving is based on the notion of storing an object graph and we are not looking to store each flash card in an individual file (although we certainly could if we wanted to). We want one object graph with references to all of our flash cards so that we can archive it into a single file.

It turns out that the FlashCards application already has such an object graph in memory in the form of the FlashCardsViewController’s NSMuteableArray property called flashCards. The flashCards array has a reference to every flash card the user has defined, so it forms the root of an object graph that contains all the flash cards. An NSMuteableArray, like all the Cocoa data structures, implements NSCoding, so we have a ready-made solution for archiving an object graph containing all the flash cards.

We need a location for the file that’ll store the flash cards. We’d like the flash cards to be safely backed up each time the user syncs her device with iTunes, so we’ll put the file in the application’s Documents directory. We can call the file anything; object archiving doesn’t put any restrictions on the filename or extension. Let’s call it FlashCards.dat. We’ll need the full path to this file both when we store the flash cards to the file system and when we read them from the file system, so let’s write a simple helper function that returns the path to the file. Open the FlashCardsViewController.m file in the Classes group, and add the method in Listing 16.

Listing 16.
-(NSString *)archivePath {
NSString *docDir =
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) objectAtIndex: 0];
return [docDir stringByAppendingPathComponent:@"FlashCards.dat"];
}

We need to archive the array of flash cards to the file before the application moves to the background and then unarchive the array of flash cards from the file when the application is loaded. To write an archive to a file, use the archiveRootObject:toFile method of NSKeyedArchiver. We’ll want to do this when the FlashCardsAppDelegate receives the applicationDidEnterBackground event, so define a method in the FlashCardsViewController.m file that FlashCardsAppDelegate can call to archive the flash cards:

-(void)archiveFlashCards {
[NSKeyedArchiver archiveRootObject:flashCards toFile:[self archivePath]];
}


Add the new method to the FlashCardsViewController.h file:

-(void)archiveFlashCards;

Open the FlashCardsAppDelegate.m file and update applicationDidEnterBackground so it will call the archiveFlashCards method of the FlashCardsViewController:

- (void)applicationDidEnterBackground:(UIApplication *)application {
[viewController archiveFlashCards];
}

Each time our application enters the background, whatever flash cards are in the array will be written out to the FlashCards.dat file in the Documents directory. On startup, we need to read the archive of the array from the file. To unarchive an object graph, use the unarchiveObjectWithFile method of NSKeyedUnarchiver. It’s possible this is the first time the application has ever been run, and there won’t yet be a FlashCards.dat file. In this case, unarchiveObjectWithFile returns nil and we can simply create a new, empty array like we did before the FlashCards application had data persistence. Update the viewDidLoad method of the FlashCardsViewController.m file as follows:

- (void)viewDidLoad {
self.flashCards = [NSKeyedUnarchiver
unarchiveObjectWithFile:[self archivePath]];
self.currentCardCounter = -1;
if (self.flashCards == nil) {
self.flashCards = [[NSMutableArray alloc] init];
}
[self showNextCard];
[super viewDidLoad];
}

That’s all there is to it. When the model objects of an application all implement NSCoding, object archiving is a simple and easy process. With just a few lines of code, we were able to persist the flash cards to the file system. Run the application and give it a try!

You may notice that, if you’re running under iOS 4.x or later, you’ll need to force the application to quit using the iOS Task Manager before you can fully test that data archiving is working. This is because iOS 4 doesn’t quit your application; it suspends it and moves it to the background!

 
Others
 
- iPhone Application Development : Implementing File System Storage (part 3) - Implementing the Application Logic
- iPhone Application Development : Implementing File System Storage (part 2) - Adding a Create Card View Controller
- iPhone Application Development : Implementing File System Storage (part 1)
- iPhone Application Development : Understanding the iPhone File System Sandbox
- Enterprise Security on the Mobile OS (part 2) - Application Sandboxing, Signing, and Permissions
- Enterprise Security on the Mobile OS (part 1) - Device Security Options & Encryption
- Mobile Geolocation : Geolocation Implementation & Risks of Geolocation Services
- Windows Phone 7 : Spicing Up the User Interface with the Silverlight Toolkit (part 3)
- Windows Phone 7 : Spicing Up the User Interface with the Silverlight Toolkit (part 2)
- Windows Phone 7 : Spicing Up the User Interface with the Silverlight Toolkit (part 1)
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us