Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

AlbuquerqueApac

macrumors member
Original poster
Jan 13, 2012
47
0
Ok, so I will preface this by saying this is NOT my code and I am doing this exercise to help learn. In fact, this came from a book and I have read over it several times and it appears correct.

ViewController.m
Code:
//
//  ViewController.m
//  Calculator
//
//  Created by on 5/1/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "ViewController.h"
#import "Calculator.h"
#import "Fraction.h"


@implementation ViewController {




//instance variables (though I think I would of set these up as object properties

char op;
int currentNumber;
BOOL firstOperand, isNumerator;

Calculator *myCalculator;   //I'm not sure why this isn't instantiated as an //object?
    

NSMutableString *displayString;

}

@synthesize display;


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    //overrided the initiall loading
    
    firstOperand = YES;
    isNumerator = YES;

   displayString = [NSMutableString stringWithCapacity:40];
    
   Calculator *myCalculator = [[Calculator alloc]init];
 
   //I'm guessing the above is the reference for the myCalculator object in //"clickEQUALS"
 
}

-(void)processDigit:(int)digit
{
    currentNumber = currentNumber * 10 + digit;
    
    [displayString appendString: [NSString stringWithFormat: @"%i", digit]];
    
    display.text = displayString;
    
}

- (IBAction)clickDigit:(UIButton *)sender
{
    
    int digit = sender.tag; 
    
    [self processDigit:digit]; 
    
}

-(void) processOp:(char)theOp
{
    NSString *opString;
    
    op = theOp;
    
    switch (theOp) { 
        case '+': 
            opString = @"+";
            break;
        case '-':
            opString = @"-";
            break;
        case '*':
            opString = @"x";
            break;
        case '/':
            opString = @"/";
            break;
            
    }

    [self storeFracPArt];
    
    firstOperand = NO;
    isNumerator = YES; 
    
    [displayString appendString: opString];
    
    display.text = displayString; 
    
}

-(void) storeFracPArt
{
    if (firstOperand) { 
        if (isNumerator) {
            myCalculator.operand1.numerator = currentNumber;
            myCalculator.operand1.denominator = 1;}
        else 
            myCalculator.operand1.denominator = currentNumber; 
        }
    else if (isNumerator) { 
        myCalculator.operand2.numerator = currentNumber;
        myCalculator.operand2.denominator= 1;
        }
    else
    { 
        myCalculator.operand2.denominator = currentNumber;
        firstOperand = YES; 
    }
    currentNumber = 0;
    
}
 
-(IBAction)clickOver
{
    [self storeFracPArt];
    isNumerator = NO;
    [displayString appendString: @"|"];
    display.text = displayString;
}

//arithmetic operations

-(IBAction) clickPlus
{
    [self processOp: '+'];
    
}

-(IBAction)clickMinus
{
    [self processOp: '-'];
    
}

-(IBAction)clickMultiply
{
    [self processOp: '*'];
    
}

-(IBAction)clickDivide
{
    [self processOp: '/'];
    
}

//misc buttons 

-(IBAction)clickEquals
{
    if ( firstOperand == NO) {
        
        //Calculator *myCalculator = [[Calculator alloc]init];
        
        [self storeFracPArt];
        [myCalculator performOperation: op];
        [displayString appendString: @"="];
        
        [displayString appendString: [myCalculator.accumulator convertToString]];
        
        display.text=displayString;
        
        
        currentNumber=0;
        isNumerator=YES;
        firstOperand=YES;
       [displayString setString:@""];
        
    
        
        
        
        
    }
    
    
}

-(IBAction)clickClear
{
    isNumerator= YES;
    firstOperand = YES;
    currentNumber = 0;
    [myCalculator clear];
    [displayString setString:@""];
    display.text=displayString;
    
    
}




- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
	[super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
	[super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

@end

when I run this in the iOS simulator and click the equals button I get the following exception:

'NSInvalidArgumentException', reason: '-[__NSCFString appendString:]: nil argument'

for the following line of code

Code:
    [displayString appendString: [myCalculator.accumulator convertToString]];

Ok, so I thought "The myCalculator" reference must not be instantiated and is set to nil.


So, I instantiate it in "clickEquals" (I get a warning that says I'm masking my instance variable (though Xcode won't let me instantiate that one)) and it works. It clears up the exception.

BUT there is a bit of a scope problem.

So I need some help.

:)
 
Your description of the code doesn't match what's actually in the code...

Here's what you described:
Ok, so I thought "The myCalculator" reference must not be instantiated and is set to nil.

But that's not what's in the code, because your viewDidLoad method instantiates it:

Code:
- (void)viewDidLoad
{
    //overrided the initiall loading
    
    firstOperand = YES;
    isNumerator = YES;

   displayString = [NSMutableString stringWithCapacity:40];
    
   [b]Calculator *myCalculator = [[Calculator alloc]init];[/b]
 
   //I'm guessing the above is the reference for the myCalculator object in //"clickEQUALS"
 
}

Then you go on to say this:
So, I instantiate it in "clickEquals" (I get a warning that says I'm masking my instance variable (though Xcode won't let me instantiate that one)) and it works. It clears up the exception.

This doesn't quite match what you actually have posted in the code, either (because the line is commented out.)
Code:
-(IBAction)clickEquals
{
    if ( firstOperand == NO) {
        
        [b]//Calculator *myCalculator = [[Calculator alloc]init];[/b]
        
        [self storeFracPArt];
        [myCalculator performOperation: op];
        [displayString appendString: @"="];
        
        [displayString appendString: [myCalculator.accumulator convertToString]];
        
        display.text=displayString;
        
        currentNumber=0;
        isNumerator=YES;
        firstOperand=YES;
       [displayString setString:@""];
    }
}

As far as I can tell, the code you've posted looks perfectly alright (I didn't actually try copying and pasting it into Xcode though,) but because your description of the code doesn't match what you actually posted for code, I wonder if maybe you have a mistake in Xcode that was lost when you copied the code over to the forums?
 
It always helps if you can be as specific as possible about the resources you are using. For books: title, author, edition. In this case, you might as well provide the chapter number / page numbers, as well.


The book is Stephen G. Kochan's Programming in Objective-C (4th edition) and the excercise begins on 464 and continues for the remainder of the book.

----------

ArtofWar:

yeah, I commented out the line because defining it locally like that serves no purpose. Sure, it doesn't throw an exception, but none of the other methods can access it.

A good starting point would be:

Can I instantiate the myCalculator globally in the implementation file? For some reason, Xcode only allows me to create it as a nil pointer.
 
Ok, so I have confirmed what I thought to be the case:

Code:
@implementation ViewController 




//instance variables (though I think I would of set these up as object properties

{
char op;
int currentNumber;
BOOL firstOperand, isNumerator;

Calculator *myCalculator; 



NSMutableString *displayString;

}

@synthesize display;


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    //overrided the initiall loading
    
    firstOperand = YES;
    isNumerator = YES;

   displayString = [NSMutableString stringWithCapacity:40];
    
   Calculator *myCalculator = [[Calculator alloc]init];
}

-(void)processDigit:(int)digit
{
    currentNumber = currentNumber * 10 + digit;
    
    [displayString appendString: [NSString stringWithFormat: @"%i", digit]];
    
    display.text = displayString;
    
}

- (IBAction)clickDigit:(UIButton *)sender
{
    
    int digit = sender.tag; 
    
    [self processDigit:digit]; 
    
}

-(void) processOp:(char)theOp
{
    NSString *opString;
    
    op = theOp;
    
    switch (theOp) { 
        case '+': 
            opString = @"+";
            break;
        case '-':
            opString = @"-";
            break;
        case '*':
            opString = @"x";
            break;
        case '/':
            opString = @"/";
            break;
            
    }

    [self storeFracPArt];
    
    firstOperand = NO;
    isNumerator = YES; 
    
    [displayString appendString: opString];
    
    display.text = displayString; 
    
}

-(void) storeFracPArt
{
    if (firstOperand) { 
        if (isNumerator) {
            myCalculator.operand1.numerator = currentNumber;
            myCalculator.operand1.denominator = 1;}
        else 
            myCalculator.operand1.denominator = currentNumber; 
        }
    else if (isNumerator) { 
        myCalculator.operand2.numerator = currentNumber;
        myCalculator.operand2.denominator= 1;
        }
    else
    { 
        myCalculator.operand2.denominator = currentNumber;
        firstOperand = YES; 
    }
    currentNumber = 0;
    
}
 
-(IBAction)clickOver
{
    [self storeFracPArt];
    isNumerator = NO;
    [displayString appendString: @"|"];
    display.text = displayString;
}

//arithmetic operations

-(IBAction) clickPlus
{
    [self processOp: '+'];
    
}

-(IBAction)clickMinus
{
    [self processOp: '-'];
    
}

-(IBAction)clickMultiply
{
    [self processOp: '*'];
    
}

-(IBAction)clickDivide
{
    [self processOp: '/'];
    
}

//misc buttons 

-(IBAction)clickEquals
{
    if ( firstOperand == NO) {
        
        NSString *calc = [NSString stringWithFormat:@"%@", myCalculator];
                          
        display.text=calc;
        
        
        
         
       
        /*
        
        [self storeFracPArt];
        [myCalculator performOperation: op];
        [displayString appendString: @"="];
        
        [displayString appendString: [myCalculator.accumulator convertToString]];
        
        display.text=displayString;
        
        
        currentNumber=0;
        isNumerator=YES;
        firstOperand=YES;
       [displayString setString:@""];
        
    
        */
        
        
        
    }
    
    
}

-(IBAction)clickClear
{
    isNumerator= YES;
    firstOperand = YES;
    currentNumber = 0;
    [myCalculator clear];
    [displayString setString:@""];
    display.text=displayString;
    
    
}




- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
	[super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
	[super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

@end

Ok, so after making these changes, I hit equals and.....

I get : (NULL)

So.... I figured out my problem, just need to fix it.

:S
 
Code:
- (void)viewDidLoad
{
    //overrided the initiall loading
    
    firstOperand = YES;
    isNumerator = YES;

   displayString = [NSMutableString stringWithCapacity:40];
    
   [COLOR="Red"]Calculator *myCalculator = [[Calculator alloc]init];[/COLOR]
}

You sure the last line of your viewDidLoad method (highlighted in red above) matches that from the book?
 
haha! That was it! THANK YOU!

I guess writing

Code:
Calculator *myCalculator = [[ Calculator alloc] init];

instead of

(no pointer or class declaration )

myCalculator = [[ Calculator alloc] init];

actually created a separate (or maybe masked) the instance variable I declared

Code:
Calculator *myCalculator;


THANK YOU GUYS!
 
haha! That was it! THANK YOU!

I guess writing

Code:
Calculator *myCalculator = [[ Calculator alloc] init];

instead of

(no pointer or class declaration )

myCalculator = [[ Calculator alloc] init];

actually created a separate (or maybe masked) the instance variable I declared

Code:
Calculator *myCalculator;


THANK YOU GUYS!

It actually created a separate, local-scope (to the viewDidLoad method) variable that, because it had the same name, masked your instance variable (hence the warning). I think it's important for you to understand this and suggest you go over your code and the fundamentals and be sure you do.
 
I understand. I was masking my variable.

I do have a question. Can you instantiate the myCalculator object at the instance variable declaration? Or must I do it as the view loads?
 
Last edited:
I do have a question. Can you instantiate the myCalculator object at the instance variable declaration? Or must I do it as the view loads?

You can't instantiate objects in a declaration, since it's just a declaration, not any kind of implementation (i.e. no code runs there). And no, you don't need to do it in your viewDidLoad. You just should do it before you first need to access it. It's pretty normal for viewDidLoad to be the place for this (as long as you are de-instantiating it in your viewDidUnload), but it is also quite common to see it done in the init method (or any of its variants).
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.