RaiseMen exception


#1

I closely followed the instructions in the book to create the RaiseMan application. I think I have wired up all the outlets and created possible bindings listed in the tutorials. However, I could not add any new item into the array. The NSTableView is empty when it appears and if I click add it throws exceptions like this;

RaiseMen[2168:303] *** -[NSKeyValueSlowMutableArray insertObject:atIndex:]: value for key employees of object 0x100153f70 is nil 2013-03-18 23:26:19.952 RaiseMen[2168:303] ( 0 CoreFoundation 0x00007fff8a4b20a6 __exceptionPreprocess + 198 1 libobjc.A.dylib 0x00007fff836333f0 objc_exception_throw + 43 2 CoreFoundation 0x00007fff8a4b1e7c +[NSException raise:format:] + 204 3 Foundation 0x00007fff83cc5029 -[NSKeyValueSlowMutableArray _raiseNilValueExceptionWithSelector:] + 114 4 Foundation 0x00007fff83cc5329 -[NSKeyValueSlowMutableArray insertObject:atIndex:] + 143 5 Foundation 0x00007fff83cc5fce -[NSKeyValueNotifyingMutableArray insertObject:atIndex:] + 136 6 AppKit 0x00007fff8b2080f5 -[NSArrayDetailBinder _performArrayBinderOperation:singleObject:multipleObjects:singleIndex:multipleIndexes:selectionMode:] + 928 7 AppKit 0x00007fff8b208621 -[NSArrayDetailBinder insertObjectIntoMasterArrayRelationship:atIndex:selectionMode:] + 51 8 AppKit 0x00007fff8b203db8 -[NSArrayController _insertObject:atArrangedObjectIndex:objectHandler:] + 140 9 AppKit 0x00007fff8b203b72 -[NSArrayController addObject:] + 162 10 AppKit 0x00007fff8b2050fc -[NSArrayController _executeAdd:didCommitSuccessfully:actionSender:] + 205 11 AppKit 0x00007fff8b59f36a _NSSendCommitEditingSelector + 58 12 AppKit 0x00007fff8b29de24 -[NSController _controllerEditor:didCommit:contextInfo:] + 190 13 CoreFoundation 0x00007fff8a4a563c __invoking___ + 140 14 CoreFoundation 0x00007fff8a4a54d7 -[NSInvocation invoke] + 263 15 CoreFoundation 0x00007fff8a4a56a9 -[NSInvocation invokeWithTarget:] + 57 16 Foundation 0x00007fff83c13db5 __NSFireDelayedPerform + 358 17 CoreFoundation 0x00007fff8a46eda4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20 18 CoreFoundation 0x00007fff8a46e8bd __CFRunLoopDoTimer + 557 19 CoreFoundation 0x00007fff8a454099 __CFRunLoopRun + 1513 20 CoreFoundation 0x00007fff8a4536b2 CFRunLoopRunSpecific + 290 21 HIToolbox 0x00007fff862490a4 RunCurrentEventLoopInMode + 209 22 HIToolbox 0x00007fff86248d84 ReceiveNextEventCommon + 166 23 HIToolbox 0x00007fff86248cd3 BlockUntilNextEventMatchingListInMode + 62 24 AppKit 0x00007fff8afac613 _DPSNextEvent + 685 25 AppKit 0x00007fff8afabed2 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128 26 AppKit 0x00007fff8afa3283 -[NSApplication run] + 517 27 AppKit 0x00007fff8af47cb6 NSApplicationMain + 869 28 RaiseMen 0x00000001000013e2 main + 34 29 libdyld.dylib 0x00007fff888f87e1 start + 0 )

I think, it might be throwing the exception as it tried to add nil value into the array. Is there something that I have missed here. I am kind of stuck here. I would be very happy to have your guidance on this.


#2

Don’t add a nil value to an array, as nil is used as an end of arg-list marker in one of the array creation methods.

If you must add a nil, then add an instance of NSNull: [NSNull null].

See: NSNull Class Reference.


#3

I did not add nil value myself. I made all the connections and set up as shown in the book and after I finished it and tries to add a person, it threw nil exception. I might have missed something, I will do this again from the beginning and see if it works.


#4

I am having a similar issue as the OP. I have checked and double checked the code, and I think I built the connections right in IB but when I build and run at the stage right before the section on Key-Value Coding and nil. (page 139) the app build and runs successfully, however when I click Add Employee, the debugger kicks in and I hit a breakpoint at the Person.m init method when self = [super init];.

po self shows $2 0x00000001001469f0 <Person: 0x1001469f0>
po personName shows $0= 0x000000000000

At this point if in the debugger I click continue program, it continues fine adds New Employee to the table etc, until I click add again, then the cycle repeats.

I have no manual breakpoints or anything set.

Am I missing something obvious here?


#5

If you are not using ARC, make sure that your setEmployees: method looks like this:

- (void)setEmployees:(NSMutableArray *)a
{
    if (a == employees)
        return;
    
    [employees release];
    employees = [a retain];
}

Retain the new array object that is passed in, and release the old one.


#6

Thanks, I am using ARC though so that shouldn’t be an issue, I wouldn’t think. I’ll go thru it again later on today and see what I am overlooking.


#7

Ok. I am going to cry uncle. I have gone over the code again and rebuilt the UI and still when I run the app and click Add Employee I get the debugger and a breakpoint at:

- (id)init { [b][self = [super init];[/b] if (self) { expectedRaise= 0.05; personName = @"New Person"; } return self; }

If I click Continue Execution the app runs. (It stops at the same point every time I click the Add button. Remove works fine. Any suggestions while I still have some hair left? :confused:


#8

Please post your .h and .m files.


#9

Thanks ibex. Here they are:

[code]//
// Person.h
// RaiseMan
//

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
NSString *personName;
float expectedRaise;
}

@property (readwrite, copy) NSString *personName;
@property (readwrite) float expectedRaise;

@end[/code]

[code]//
// Person.m
// RaiseMan
//

#import “Person.h”

@implementation Person

@synthesize personName;
@synthesize expectedRaise;

  • (id)init
    {
    self = [super init];
    if (self)
    {
    expectedRaise= 0.05;
    personName = @“New Person”;
    }
    return self;
    }

  • (void) setNilValueForKey:(NSString *)key
    {
    if ([key isEqual:@“expectedRaise”])
    {
    [self setExpectedRaise:0.0];
    } else
    {
    [super setNilValueForKey:key];
    }
    }

@end
[/code]

[code]//
// RMDocument.h
// RaiseMan
//

#import <Cocoa/Cocoa.h>

@interface RMDocument : NSDocument
{
NSMutableArray *employees;
}

  • (void) setEmployees:(NSMutableArray *)a;

@end[/code]

[code]//
// RMDocument.m
// RaiseMan
//

#import “RMDocument.h”

@implementation RMDocument

  • (id)init
    {
    self = [super init];
    if (self) {
    // Add your subclass-specific initialization here.
    employees = [[NSMutableArray alloc] init];
    }
    return self;
    }

  • (void) setEmployees:(NSMutableArray *)a
    {
    // This is an unusual setter method. We are going to add a lot
    // of smarts to it in the next chapter.
    if (a==employees)
    return;
    employees = a;
    }

  • (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 @“RMDocument”;
    }

  • (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.
    }

  • (BOOL)autosavesInPlace
    {
    return YES;
    }
  • (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
    {
    // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
    NSException *exception = [NSException exceptionWithName:@“UnimplementedMethod” reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
    @throw exception;
    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 outError != NULL, ensure that you create and set an appropriate error when returning NO.
    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
    // If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.
    NSException *exception = [NSException exceptionWithName:@“UnimplementedMethod” reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
    @throw exception;
    return YES;
    }

@end
[/code]


#10

Your code is okay. I tested it and I can’t see anything wrong with it; I can add and delete employees.

The problem you are experiencing is very a odd one and hard to tell from here.

My guess is that something went wrong when you set up connections in Interface Builder or there is a more sinister problem.


#11

Thanks ibex. I am not sure what the deal was. I verified all the IB binding etc and they were right. Anyway I went on with the rest of the project in the later chapters and for some reason after adding the Undo/Redo code the problem disappeared. Well, I’ll chalk it up to one of the great mysteries of the universe. :slight_smile:

I really like XCode, but it does have some quirks, like one line that the build would fail on. It was a just [super observeValueForKeyPath:keyPath …] and the compiler said it was expecting an expression. checked and rechecked it but no problem. Cut and pasted the line back in and viola it built successfully.


#12

I got same result with Xcode 9.2 (9C40b) on high sierra 10.13.3

but with this project ( https://github.com/jtatum/cocoa/tree/master/RaiseMan/RaiseMan )
it runs well.

still can’t find any difference.