Flatten the Object Network: Coding and Archiving

Virtually all applications need a way to make some of their objects persistent. For example, the Expenses application that you created in the previous section doesn’t save the state of the data model, so you’ll lose all of your expenses information as soon as you quit. Cocoa applications typically use coding and archiving to store document contents and other critical application data to disk for later retrieval. Some applications may also use coding and archiving to send objects over a network to another application. (For instance, in case you want to send a set of expenses to a friend.)

In this section you will modify the simple expense-tracking application from the previous section to use coding and archiving so it can save and reload the array of expenses.

NSCoder and NSArchiver

Coding, as implemented by NSCoder, takes a connected group of objects such as those in an application (an object graph) and serializes that data, capturing the state, structure, relationships, and class memberships of the objects. A subclass of NSCoder, NSArchiver extends this behavior by storing the serialized data in a file.

When archiving an object graph’s root object, archive not only that object, but all other objects the root object references, all objects those second-level objects reference, etc. To be archived, though, objects must conform to the NSCoding protocol (consisting of the encodeWithCoder: and initWithCoder: methods).

Add Coding and Archiving to the Expenses Application

  1. Open the main nib file in Interface Builder.

  2. In the Instances pane of the MainMenu.nib window, Control-drag a connection from the Window instance to the MyDataSource instance. In the Connections pane of the Info window, make MyDataSource the delegate of Window.

  3. In Project Builder, open the TableView example project.

  4. Open Expense.h and modify the class declaration as follows. Adding <NSCoding> declares that the Expense class conforms to the coding protocol.

    @interface Expense : NSObject <NSCoding>
    {
  5. Open Expense.m and add the NSCoding methods. The most frequently used NSCoder method, encodeObject:, encodes (serializes) a single object. For nonobject types, you can use encodeValueOfObjCType:at:. The order of decoding should be the same as the order of encoding; since date is encoded first, it should be decoded first. NSCoder defines decode methods that correspond to the encode methods, which you should use. As in any init method, end by returning self—an initialized instance:

    - (id)initWithCoder:(NSCoder *)coder {
        [self setDate: [coder decodeObject]];
        [self setCategory: [coder decodeObject]];
        [self setAmount: [coder decodeObject]];
        return self;
    }
    - (void)encodeWithCoder:(NSCoder *)coder {
        [coder encodeObject: [self date]];
        [coder encodeObject: [self category]];
        [coder encodeObject: [self amount]];
    }
  6. Add the following implementation of the windowShouldClose: delegate method to MyDataSource. This method first displays a standard alert asking users if they want to save the contents of the window. If they click Save, a Save dialog box (NSSavePanel) is displayed so the users can name the file and choose a directory in which to save it. The setRequiredFileType: method tells the Save panel to append .expenses to the filename so that the file has a recognizable type. If the user clicks Save, NSArchiver encodes the entire array of Expense objects and saves them to disk. If the user cancels, the window is closed and nothing is saved:

    - (BOOL)windowShouldClose:(NSWindow *)sender
    {
        NSSavePanel *sp;
        int answer;
    
        answer = NSRunAlertPanel(@"Save Expenses",
            @"Do you want to save?", 
            @"Save", 
            @"Don't Save", 
            nil);
    
        if (answer == NSAlertDefaultReturn) {
            sp = [NSSavePanel savePanel];
            [sp setRequiredFileType:@"expenses"];
            answer = [sp runModal];
            if (answer == NSOKButton ) {
                [NSArchiver archiveRootObject:[self expenses]
                        toFile:[sp filename]];
            }
        }
    
        return YES;
    }
  7. Finally, modify the implementation of awakeFromNib so it prompts the users for a file to load. This method now presents a standard open dialog box, so the users can choose an archived data file to load. The types array means that the users can’t choose any file, only valid expenses files (or at least files with a .expenses file extension). If they choose a file, NSUnarchiver is invoked to restore the array of expense objects from the archive. If the users cancel, the test data is generated as before:

    - (void)awakeFromNib
    {
        NSOpenPanel *op;
        int answer;
    
        op = [NSOpenPanel openPanel];
        answer = [op runModalForTypes: [NSArray arrayWithObject:@"expenses"]];
    
        if (answer == NSOKButton) {
            [self setExpenses: [NSUnarchiver unarchiveObjectWithFile:
                    [op filename]]];
        } else {
            [self setExpenses:[self generateTestData]];
        }
    }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.2.78