Cocoa Programming for Mac OS X - Aaron Hillegass [57]
Loading
The Open..., Open Recent, and Revert To Saved menu items, although different, all deal with the same basic problem: getting the model from a file or file wrapper. To handle these menu items, your NSDocument subclass must implement one of three methods:
- (BOOL)readFromData:(NSData *)data
ofType:(NSString *)typeName
error:(NSError **)outError
Your document is passed an NSData object that holds the contents of the file that the user is trying to open. Return YES if you successfully create a model from the data. If you return NO, the user will get an Alert panel that should explain why it was unable to parse the file. The contents of the Alert panel will be determined by the NSError object you give it.
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper
ofType:(NSString *)typeName
error:(NSError **)outError;
Your document reads the data from an NSFileWrapper object.
- (BOOL)readFromURL:(NSURL *)absoluteURL
ofType:(NSString *)typeName
error:(NSError **)outError;
Your document object is passed a URL (usually just a path to a file on the filesystem). The document reads the data from the file.
After implementing one save method and one load method, your document will know how to read from and write to files. When opening a file, the document will read the document file before reading the NIB file. As a consequence, you will not be able to send messages to the user interface objects immediately after loading the file (because they won’t exist yet). To solve this problem, after the NIB file is read, your document object is sent the following method:
- (void)windowControllerDidLoadNib:(NSWindowController *)x;
In your NSDocument subclass, you will implement this method to update the user interface objects.
If the user chooses Revert To Saved from the menu, the model is loaded, but windowControllerDidLoadNib: is not called. You will, therefore, also have to update the user interface objects in the method that loads the data, just in case it was a revert operation. One common way to deal with this possibility is to check one of the outlets set in the NIB file. If it is nil, the NIB file has not been loaded, and there is no need to update the user interface.
NSWindowController
The final class in the document architecture that we might discuss would be NSWindowController, but you will not initially need to worry about it. For each window that a document opens, it will typically create an instance of NSWindowController. As most applications have only one window per document, the default behavior of the window controller is usually perfect. Nevertheless, you might want to create a custom subclass of NSWindowController in the following situations:
• You need to have more than one window on the same document. For example, in a CAD program you might have a window of text that describes the solid and another window that shows a rendering of the solid.
• You want to put the user interface controller logic and model controller logic into separate classes.
• You want to create a window without a corresponding NSDocument object. You will do this in Chapter 12.
Saving and NSKeyedArchiver
Now that you have taught your object to encode and decode itself, you will use it to add saving and loading to your application. When it is time to save your people to a file, your RMDocument class will be asked to create an instance of NSData. Once your object has created and returned an NSData object, it will be automatically written to a file.
To create an NSData object, you will use the NSKeyedArchiver class. NSKeyedArchiver has the following class method:
+ (NSData *)archivedDataWithRootObject:(id)rootObject
This method archives the objects into the NSData object’s buffer of bytes.
Once again, we return to the idea of “I told two friends, and they told two friends.” When you encode an object, it will encode its objects, and they will encode their objects, and so on, and so on, and so on. What you will encode, then, is the employees