Archiving and Unarchiving

pp123

Archiving preserves objects including any interrelatinships or dependencise among the archived objects. Unarchiving re-created the objects and relationships that were previously archived.

Archived objects are most commonly stored as binary data. Binary data tends to be fast to read and write from memor or disk and fast to transmit over a network. However, in cases where it's helpful to store the objects in a human readable text format, Cocoa supports archiving and unarchiving from XML files with a few llimitations.

 

Motivation

Use the Archiving and Unarchiving pattern any time you need to copy or store a group of interrelated objects. Some Cocoa applications adopt this approach to store application data in files. Because most Cocoa objects automatically work with the Archiving and Unarchiving pattern, it's straightforward to save application data by just archiving your application's objects. To reload the application data, unarchive the objects. The previous application state is retored.

The Archiving and Unarchiving pattern is somethims used to implement the "deep" copying pattern as explained in Chapter 12 "Copying."

 

Solution

The Archiving and Unarchiving pattern relies on objects to encode their own internal state into an archive and later decode themselves from an archive. By implementation their own encoding and decoding, the objects safeguard encapsulation and data hiding. Objects that reference other objects typically give the referenced objects an opportunity to encode themselves as well. There fore, the act of encoding one object may add many objecs to an archive. For example, if an object being encoded has instance variables that are objects, the instance variables are usually also encoded. If an object doesn;t encode its instance variables are usually also encoded. Or it will be incomplete when later unarchived. On the other hand, if an instance variable has a default value or can be calculated from other instance variables, it often makes sense not to encode it. Each onject must make its own determination regarding wihich instance variable to encode.

The Key to implementing the Archiving and Unarchiving pattern is the treatment of interrelated objects. No matter how complex the relationships between obects, each object in an archive is only encoded once in that archive. This minimizes the storage required for an archive, but more importantly, it simplifies the restoration of relationships when unarchinving. If many archived objects reference the same object, the unarchived copies will also all reference a single copy of that same object. Similarly, within any group of objects that are beeing archived, two or more objects might reference each other. Such circular references are resolved automatically in part because of the rule that each object is only represented once in an archive.

Built-in object versioning provides the ability to unarchive objects that were archived with different application versions or different operating system releases. Cocoa supports object substitution during unarchiving. For example,Interface Builder sometimes archives placeholder objects that have stored connections to other objects within the archive. When the objects are unarchived into a running application, an application-specific object is substituted for the placeholder, and connections set in Interface Builder end up being restored to the application-specific object. Object substitution is combined with versioning to automatically update objects as they are unarchived. Consider what happens when a class is deprecated or replaced between version 1.0 and version 2.0 of an application. When the version 2.0 application unarchives an instance of the old class, the application can opptionally substitute an instance of the new class and perform any necessary data conversions during the substitution.

 

Conditional Encoding

Conditional encoding constrains which objets are archived in situations where many objects are inter-related but not all relationships need to be preserved. Objects an be either unconditionallly encoded or conditionally encoded. When an object is unconditionally encoded, it's always added to the archive if it isn't already there. When unarchived, all objects that referenced the unconditionally encoded object have their  references restored. Conditional encoding of an object means that references to the object should only be restored if the object ends up in the archive. To be in an archive, an object has to be unconditionally encoded at least once. If a conditionally encoded object does end up in an archive, then references to the object are restored as normal when unarchived. However, if the obejct doesn't make it into the archive, all objects that formerly referenced that conditionally encoded object have their references set to nil when unarchived.

pp126 Figure 11.1 that illustrates what happened.

However, if an object is unconditionally encode by at least one referencing object, then when decoded, all references are restored regardless of whether each reference was conditional or unconditional. (Already in an archive)

Use conditional encoding when only a subset of the objects in a group needs to be archived.   For example, each Cocoa NSView object has  a single reference to its "parent" ("superview") and references to all of its "Child" views ("subviews"). The relationships between Cocoa NSView instances are explained in Chapter 16, "Hierarchies." Each view conceptually "owns" its subviews and treats them as an intrinsic part itself. Therefore, when an NSView instanc encodes itself, all of the view's subviews are unconditionallly encoded. In contrast, views have little knowledge aout their superview and work correctly regardless of the specific view that owns them. NSView conditionally encodes its superview. 

The fact that NSView conditionally encodes its superview enables you to archive a portion of a view hierarchy without having to archive the entire hierarchy. If you choose to archive a view in the middle of a hierarchy, the view that you archive will encode all of its subviews but not necessarily its superview.

 

Examples in Cocoa

The best way to create an archive of interrelated objects is to use the NSKeyedArchiver class.

[NSKeyedArchiver archivedDataWithRootObject:someObject];

The +(NSData *)archivedDataWithRootObject:(id)rootObject method sends a message asking the specified root object to unconditionally encode itself. The root object in turn asks any referenced objects to either conditionally or unconditionally encode themselves. 

The root object can be any object that conforms to the NSCoding protocol including an instance of NSArray or NSDictionary. The NSData instance returned from +archivedDataWithRootObject: may be stored in a file, as a Core Data attribute, or as a default value in user defaults.??

 

Unarchive an object with NSKeyedUnarchiver's +(id)unarchiveOjectWithData:(NSData *)data method as follows:

[NSKeyedUnarchiver unarchiveObjectWithData:someData];

NSKeyedUnarchiver asks the previously encoded root object to decode itself. In the process of decoding itself, the root object asks referenced objects to decode themselves. As a result, the entire hierarchy of encoded objects is decoded, and the relationships between objects are restored.

 

 

To see how the Archiving and Unarchiving pattern is used in practive, consider how a Cocoa application stores a color in user defaults. User defaults provide a standard way to store system, user ,and application preferences or default values. You access user defualts via the NSUserDefaults class, which uses the Associative Storage pattern from Chapter 19 to store key value pairs. 

Code like the following can store a string value in user defaults:

[[NSUserDefaults standardUserFefaults]

  setObject:@"http://www.stepwise.com"

  forKey:@"homePage"];

However, NSUserDefaults is only able to store objects that are instances of the following classes: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. Cocoa encapsulates colors with the NSColor class, which is not in the list of classes directly supported by NSUserDefaults. If you want to store a color i user defaults, combine the Category pattern from chapter 6, "Category," with Archiving and Unarchiving to exend NSUserDefaults. The following category methods take advantage of the fact that NSColor objects already conform to the NSCoding protocol to store and retrieve any NSColor in user defaults:

 @implementation NSUserDefaults  (ColorHandling)

-  (void)setColor: (NSColor *)theColor forKey: (NSString *)key

{

  NSData *data = [NSKeyedArchiver archivedDataWithRootObject: theColor];

  [self setObject:data forKey:key];

}



-  (NSColor *)colorforKey: (NSString *)key
{

  NSData *data = [self dataForKey:key];

  return [NSKeyedUnarchiver unarchiveObjectWithData:data];

}

@end

 Using a similar technique, any object that conforms to the NSCoding protocol can be archived and stored in user defaults as NSData.

 

Implementing the NSCoding Protocol

The NSCoding protocol defines only two methods,

-(void)encodeWithCoder(NSCoder *)coder    //encode object by themselves

and -(id)initWithCoder:(NSCoder *)coder  //decode

NSObject base class doesn't conform to the NSCoding protocol, but most other Cocoa classes do. If your class inherits from a Cocoa class that already conforms to the NSCoding protocol, you must override the NSCoding methods to call the inherited implementations and then encode or decode the unique information for your class.

Note:

The -encodeWithCoder: and -initWithCoder: methods are prime examples of the Template Method pattern in Chapter 4, "Template Method." These methods are overriden in each subclass you create, and the methods are called automatically by Cocoa when appropriate. You shouldn't call these methods directly except to invoke the superclass' implementation from within an override.

 

 

pp128 WordInformation class as example for implementing NSCoding protocol.

 

 

 Nib Awaking

A problem can arise when objects that have been encoded into an Inteface Builder .nib file are decode. As an object is decoded, it might need to send messages to a referenced object that has not yet been decoded. How does an object know when during decoding it is safe to access the objects to which it has relationships? The answer is the -awakeFromNib method.

When objects are decoded from a .nib file, the Application Kit automatically sends the -awakeFromNib message to every decoded object that can respond to it. The -awakeFromNib message is only called after all the objects in the archive have been decoded and initialized. Therefore, when an object receives an -awakeFromNib message, it's guaranteed to have all its outlet instance variables set  ?ok

 

Consequences

Archiving and Unarchiving provide a convenient standardway to preserve or copy interconneced objects; The pattern supports Cocoa's implementation of distributed messaging. 

Most Cocoa classes conform to the NSCoding protocol and can therefore be used with Archiving and Unarchiving.

 

 

 

 

 

 

 

 

posted on 2013-02-24 17:47  Chansonyan  阅读(252)  评论(0)    收藏  举报

导航