Joel Derfner
Joel Derfner

Reputation: 2207

key-value coding compliance

I was foolish and didn't test continually as I programmed, so now I'm not sure where the error has crept in. Am working on a programmable calculator. When I run, it crashes before displaying anything and gives me this message:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<CalculatorViewController 0x6a405a0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key description.'

I'd use NSLog to look for the bug, but I don't know where to try it when the crash happens before anything shows up. Thoughts on what I'm doing wrong?

Here's CalculatorViewController.m, with some extra property declarations for unfinished, commented-out code I've omitted:

#import "CalculatorViewController.h"
#import "CalculatorBrain.h"

@interface CalculatorViewController ()
@property (nonatomic) BOOL userIsEnteringNumber;
@property (nonatomic) BOOL numberIsNegative;
@property (nonatomic,strong) CalculatorBrain *brain;
@end

@implementation CalculatorViewController
@synthesize display = _display;
@synthesize descriptionLabel = _descriptionLabel;
@synthesize userIsEnteringNumber = _userIsEnteringNumber;
@synthesize numberIsNegative;
@synthesize brain = _brain;


-(CalculatorBrain *)brain
{
if (!_brain) _brain = [[CalculatorBrain alloc] init];
return _brain;
}


//This adds a pressed digit to the display label.

- (IBAction)digitPressed:(UIButton *)sender 
{
NSString *digit = sender.currentTitle;

//Enter digit if it wouldn't create a two-decimal-point case.
NSRange range = [self.display.text rangeOfString:@"."];
if (range.location==NSNotFound || (![digit isEqualToString:@"."]))
    if (self.userIsEnteringNumber)
    {
        self.display.text = [self.display.text stringByAppendingString:digit];
        self.descriptionLabel.text = [self.display.text stringByAppendingString:@" "];
    }
    else 
    {
        self.descriptionLabel.text = [self.descriptionLabel.text stringByAppendingString:digit];
        self.descriptionLabel.text = [self.descriptionLabel.text stringByAppendingString:@" "];
        if (![sender.currentTitle isEqualToString:@"."])
        {
            self.display.text = digit;
        }
        else 
        {
            self.display.text = @"0.";
        }
        self.userIsEnteringNumber = YES;
    }
}


//This sets up an operation.

- (IBAction)operationPressed:(UIButton *)sender 
{
if (self.userIsEnteringNumber) [self enterPressed];
NSString *operation = sender.currentTitle;
double result = [self.brain performOperation:operation];
self.display.text = [NSString stringWithFormat:@"%g",result];
{
    NSString *descr = [self.brain description];
    self.descriptionLabel.text = descr;
}
}


- (IBAction)enterPressed 
{
NSCharacterSet *set = [NSCharacterSet decimalDigitCharacterSet];
NSRange range = [self.display.text rangeOfCharacterFromSet:set];
if (range.location==NSNotFound)
{
    [self.brain pushOperandAsVariable:self.display.text];
}
else 
{
    [self.brain pushOperand:[self.display.text doubleValue]];        
}

self.userIsEnteringNumber = NO;
}

@end

And here's CalculatorBrain.m:

#import "CalculatorBrain.h"

@interface CalculatorBrain()
@property (nonatomic, strong) NSMutableArray *programStack;
@property (nonatomic,strong)NSDictionary *variableValues;
@end

@implementation CalculatorBrain

@synthesize programStack = _programStack;
@synthesize variableValues = _variableValues;

- (NSMutableArray *)programStack
{
if (!_programStack) _programStack = [[NSMutableArray alloc] init];
return _programStack;
}

- (id)program
{
return [self.programStack copy];
}


//Here are the two types of pushes that the ViewController can implement.  First, operand pushes . . . 

- (void)pushOperand:(double)operand
{
[self.programStack addObject:[NSNumber numberWithDouble:operand]];
}


//. . . and then variable pushes.

- (void) pushOperandAsVariable:(NSString *)variable
{
//Create dictionary
//Move this later on to ViewController but for now leave where it is....
NSMutableArray *variablesUsed = [[NSMutableArray alloc] init];
NSArray *objects = [[NSArray alloc] initWithObjects:[NSNumber numberWithDouble:3],[NSNumber numberWithDouble:4.1],[NSNumber numberWithDouble:-6],[NSNumber numberWithDouble:4.5298], [NSNumber numberWithDouble:3.14159], nil];
NSArray *keys = [[NSArray alloc] initWithObjects:@"x",@"y",@"z",@"foo", @"π", nil];
NSDictionary *variableValues = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];

//Check program for keys
    NSNumber *operand;
for (int i=0; i<keys.count; i++)
{
    if ([[keys objectAtIndex:i] isEqual:variable])
        [variablesUsed addObject:variable];
    operand = [variableValues objectForKey:variable];
}
[self.programStack addObject:operand];
}

- (double)performOperation:(NSString *)operation
{
[self.programStack addObject:operation];
return [[self class] runProgram:self.program];
}

+ (double)popOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack) [stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]])
{
    result = [topOfStack doubleValue];
}

//Here are the results for various operations.

else if ([topOfStack isKindOfClass:[NSString class]])
{
    NSString *operation = topOfStack;
    if ([operation isEqualToString:@"+"]) 
    {
        result = [self popOffStack:stack] +
        [self popOffStack:stack];
    } 
    else if ([@"*" isEqualToString:operation]) 
    {
        result = [self popOffStack:stack] *
        [self popOffStack:stack];
    } 
    else if ([operation isEqualToString:@"-"]) 
    {
        double subtrahend = [self popOffStack:stack];
        result = [self popOffStack:stack] - subtrahend;
    } 
    else if ([operation isEqualToString:@"/"]) 
    {
        double divisor = [self popOffStack:stack];
        if (divisor) result = [self popOffStack:stack] / divisor;
    }
    else if ([operation isEqualToString:@"sin"])
    {
        result = sin([self popOffStack:stack]);
    }
    else if ([operation isEqualToString:@"cos"])
    {
        result = cos([self popOffStack:stack]);
    }
    else if ([operation isEqualToString:@"√"])
    {
        result = sqrt([self popOffStack:stack]);
    }
    else if ([operation isEqualToString:@"π"])
    {
        result = M_PI;
    }
}
return result;
}

+ (double)runProgram:(id)program
{
//Run program.
NSMutableArray *mutableCopyOfProgram;
if ([program isKindOfClass:[NSArray class]]) 
{
    mutableCopyOfProgram = [program mutableCopy];

return [self popOffStack:mutableCopyOfProgram];
}
else return 0;
}

@end

As always, thanks for your help.

Upvotes: 0

Views: 483

Answers (1)

Phillip Mills
Phillip Mills

Reputation: 31026

The most common cause of this is that you have a CalculatorViewController object defined in a storyboard or xib file and something in the graphical interface has a link to an outlet on it called "description". Meanwhile, you no longer have a description outlet in the code for that class.

This will usually be fixed by tracking down the stray reference in interface builder and getting rid of it.

Upvotes: 3

Related Questions