Problem 2


#1

I couldn’t solve the second problem. at chap. 8

and had a debugging problem like below. Could you help me?
And please help me uploading my hole project files.

2010-04-23 03:45:47.308 RaiseMan[1566:813] [<Person 0x15b650> valueForUndefinedKey:]: this class is not key value coding-compliant for the key (null).

my source code for MyDocument.h is

#import <Cocoa/Cocoa.h>

@class Person;

@interface MyDocument : NSDocument
{
NSMutableArray *employees;
IBOutlet NSTableView *tableView;

}
//- (void)setEmployees:(NSMutableArray *)array;

  • (IBAction)createEmployee:(id)sender;
  • (IBAction)deleteSelectedEmployees:(id)sender;

@end


#2

MyDocument.m is

#import “MyDocument.h”
#import “Person.h”

@implementation MyDocument

  • (id)init
    {
    if (![super init])
    return nil;

    //[self setEmployees:[NSMutableArray array]];
    employees = [[NSMutableArray alloc] init];
    if (self) {

      // Add your subclass-specific initialization here.
      // If an error occurs here, send a [self release] message and return nil.
    

    }
    return self;
    }

  • (void)dealloc
    {
    //[self setEmployees:nil];
    [employees release];
    [super dealloc];
    }

#pragma mark NSDocument methods

  • (NSString *)windowNibName
    {
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @“MyDocument”;
    }

  • (void)windowControllerDidLoadNib:(NSWindowController *) aController
    {
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document’s window.
    }

  • (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
    {
    // Insert code here to write your document to data of the specified type. If the given outError != NULL, ensure that you set *outError when returning nil.

    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.

    // For applications targeted for Panther or earlier systems, you should use the deprecated API -dataRepresentationOfType:. In this case you can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.

    if ( outError != NULL ) {
    *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
    }
    return nil;
    }

  • (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
    {
    // Insert code here to read your document from the given data of the specified type. If the given outError != NULL, ensure that you set *outError when returning NO.

    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.

    // For applications targeted for Panther or earlier systems, you should use the deprecated API -loadDataRepresentation:ofType. In this case you can also choose to override -readFromFile:ofType: or -loadFileWrapperRepresentation:ofType: instead.

    if ( outError != NULL ) {
    *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
    }
    return YES;
    }

/*

  • (void)setEmployees:(NSMutableArray *)a
    {
    if (a == employees)
    return;

    [a retain];
    [employees release];
    employees = a;
    }
    */

#pragma mark Action methods

  • (IBAction)deleteSelectedEmployees:(id)sender
    {

    NSIndexSet *rows = [tableView selectedRowIndexes];

    if ([rows count] == 0) {
    NSBeep();
    return;
    }
    [employees removeObjectsAtIndexes:rows];
    [tableView reloadData];
    }

  • (IBAction)createEmployee:(id)sender
    {
    Person *newEmployee = [[Person alloc] init];
    [employees addObject:newEmployee];
    [newEmployee release];
    [tableView reloadData];
    }

#pragma mark TableView dataSource methods

  • (int)numberOfRowsInTableView:(NSTableView *)tv
    {
    return [employees count];
    }

  • (id)tableView:(NSTableView *)tv
    objectValueForTableColumn:(NSTableColumn *)aTableColumn
    row:(int)rowIndex
    {

    NSString *identifier = [aTableColumn identifier];

    Person *person = [employees objectAtIndex];

    return [person valueForKey:identifier];
    }

  • (void)tableView:(NSTableView *)tv
    setObjectValue:(id)anObject
    forTableColumn:(NSTableColumn *)aTableColumn
    row:(int)rowIndex
    {
    NSString *identifier = [aTableColumn identifier];
    Person *person = [employees objectAtIndex];

    [person setValue:anObject forKey:identifier];
    }
    /*

  • (void)tableView:(NSTableView *)tableView
    sortDescriptorsDidChange:(NSArray *)oldDescriptors
    {
    NSArray *newDescriptors = [tableView sortDescriptors];
    [myArray sortUsingDescriptors:newDescriptors];
    [tableView reloadData];
    }
    */
    @end


#3

[quote=“fluiday”]MyDocument.m is

[code]#import “MyDocument.h”
#import “Person.h”

@implementation MyDocument

  • (id)init
    {
    if (![super init])
    return nil;

    //[self setEmployees:[NSMutableArray array]];
    employees = [[NSMutableArray alloc] init];
    if (self) {

      // Add your subclass-specific initialization here.
      // If an error occurs here, send a [self release] message and return nil.
    

    }
    return self;
    }

  • (void)dealloc
    {
    //[self setEmployees:nil];
    [employees release];
    [super dealloc];
    }

#pragma mark NSDocument methods

  • (NSString *)windowNibName
    {
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @“MyDocument”;
    }

  • (void)windowControllerDidLoadNib:(NSWindowController *) aController
    {
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document’s window.
    }

  • (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
    {
    // Insert code here to write your document to data of the specified type. If the given outError != NULL, ensure that you set *outError when returning nil.

    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.

    // For applications targeted for Panther or earlier systems, you should use the deprecated API -dataRepresentationOfType:. In this case you can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.

    if ( outError != NULL ) {
    *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
    }
    return nil;
    }

  • (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
    {
    // Insert code here to read your document from the given data of the specified type. If the given outError != NULL, ensure that you set *outError when returning NO.

    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.

    // For applications targeted for Panther or earlier systems, you should use the deprecated API -loadDataRepresentation:ofType. In this case you can also choose to override -readFromFile:ofType: or -loadFileWrapperRepresentation:ofType: instead.

    if ( outError != NULL ) {
    *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
    }
    return YES;
    }

/*

  • (void)setEmployees:(NSMutableArray *)a
    {
    if (a == employees)
    return;

    [a retain];
    [employees release];
    employees = a;
    }
    */

#pragma mark Action methods

  • (IBAction)deleteSelectedEmployees:(id)sender
    {

    NSIndexSet *rows = [tableView selectedRowIndexes];

    if ([rows count] == 0) {
    NSBeep();
    return;
    }
    [employees removeObjectsAtIndexes:rows];
    [tableView reloadData];
    }

  • (IBAction)createEmployee:(id)sender
    {
    Person *newEmployee = [[Person alloc] init];
    [employees addObject:newEmployee];
    [newEmployee release];
    [tableView reloadData];
    }

#pragma mark TableView dataSource methods

  • (int)numberOfRowsInTableView:(NSTableView *)tv
    {
    return [employees count];
    }

  • (id)tableView:(NSTableView *)tv
    objectValueForTableColumn:(NSTableColumn *)aTableColumn
    row:(int)rowIndex
    {

    NSString *identifier = [aTableColumn identifier];

    Person *person = [employees objectAtIndex];

    return [person valueForKey:identifier];
    }

  • (void)tableView:(NSTableView *)tv
    setObjectValue:(id)anObject
    forTableColumn:(NSTableColumn *)aTableColumn
    row:(int)rowIndex
    {
    NSString *identifier = [aTableColumn identifier];
    Person *person = [employees objectAtIndex];

    [person setValue:anObject forKey:identifier];
    }
    /*

  • (void)tableView:(NSTableView *)tableView
    sortDescriptorsDidChange:(NSArray *)oldDescriptors
    {
    NSArray *newDescriptors = [tableView sortDescriptors];
    [myArray sortUsingDescriptors:newDescriptors];
    [tableView reloadData];
    }
    */
    @end[/code]
    [/quote]

Your MyDocument.h and MyDocument.m look okay (although it has extra things you don’t need), but the only issue I see with these two files is at the end of MyDocument.m.

The following was an example of HOW to use it, but you copied it word-for-word:

- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors { NSArray *newDescriptors = [tableView sortDescriptors]; [myArray sortUsingDescriptors:newDescriptors]; [tableView reloadData]; }

You’ll have to change it like this in order for it to do what you want:

- (void)tableView:(NSTableView *)tableView
sortDescriptorsDidChange:(NSArray *)oldDescriptors
{
	NSArray *newDescriptors = [tableView sortDescriptors];
	[employees sortUsingDescriptors:newDescriptors];
	[tableView reloadData];
}

However, even this change doesn’t fix your error. It seems there might be something wrong with your Person class OR something else in your project is messed up.

Go into folder where your RaiseMan project is stored, and delete the Build folder inside of it. Then, inside finder, right-click on the RaiseMan folder and go to Compress “RaiseMan”. This will create a RaiseMan.zip file. Then just upload that folder somewhere and post the link here.


#4

Make sure in Interface Builder, for each column under the Attributes tab, set the “Identifier” field to the name of the variable which that column is to display. :wink: