Method returning 0 - objective-c

I'm doing the foundation calculator homework from the cs193p course, and my
+evaluateExpression:usingVariables: method doesn't work. It always returns 0.
Here's my method:
+ (double)evaluateExpression: (id)anExpression usingVariablevalues: (NSDictionary *)variables {
CalculatorBrain *worker = [[[CalculatorBrain alloc] init] autorelease];
double returnValue = 10.0;
if ([anExpression isKindOfClass:[NSMutableArray class]]) {
for (id term in anExpression) {
NSLog(#"%f", returnValue); // breakpoint
if ([term isKindOfClass:[NSString class]]) { // string
if ([term hasPrefix:#"%"] && [term length] == 2) { // variable
double value = [(NSNumber *)[variables objectForKey:[term substringFromIndex:1]] doubleValue];
NSLog(#"Variable: %#, value: %d", [term substringFromIndex:1], value);
[worker setOperand:value];
}
else if ([term length] == 1) { // operation
returnValue = [worker performOperation:term];
}
else {
NSLog(#"Invalid expression.\n\tMultiple character operation found: %#", term);
}
}
else if ([term isKindOfClass:[NSNumber class]]) { // operand
double value = [term doubleValue];
[worker setOperand:value];
}
else {
NSLog(#"Invalid expression.\n\tWrong type in expression: %#, The value is: %#.",[term class], term); // Wrong type in expression
}
}
}
else {
NSLog(#"Invalid expression.\n\tThe expression is of the wrong type: %#.", [anExpression class]); // expression is of wrong type
}
//returnValue = worker.operand;
return returnValue;
}
Any hints? it always returns 0.
/* Ouput from Console */
2012-06-27 20:01:05.487 Calculator[2823:207] 0
2012-06-27 20:01:05.488 Calculator[2823:207] 0
2012-06-27 20:01:05.489 Calculator[2823:207] 0
2012-06-27 20:01:05.490 Calculator[2823:207] Variable: x, value: 0
2012-06-27 20:01:05.490 Calculator[2823:207] 0
2012-06-27 20:01:05.491 Calculator[2823:207] 0
2012-06-27 20:01:05.491 Calculator[2823:207] 0
Note: I set returnValue to 10.0 at the beginning to check if it is getting changed at all.
Update:
I found out that it gets 0 from the dictionary. I think the calling code is the cause:
- (IBAction) performSampleExpression {
NSMutableArray *expr = [[NSMutableArray alloc] init];
[expr addObject:[NSNumber numberWithDouble:3.0]];
[expr addObject:#"+"];
[expr addObject:#"%x"];
[expr addObject:#"*"];
[expr addObject:[NSNumber numberWithDouble:4.0]];
NSDictionary *varsdict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:10.0], #"x", nil];
double result = [CalculatorBrain evaluateExpression:expr usingVariablevalues:varsdict];
if (!result) NSLog(#"result = nil");
NSLog(#"%f", result);
display.text = [NSString stringWithFormat:#"%f", result];
[expr release];
}
Result isn't nil though.
Update 2:
Using the debugger (nice hint. This might solve it.) I found something strange:
On this line: (first time)
returnValue = worker.operand;
It says returnValue is still 10, and not three (worker.operand IS).
Is the assignment failing, or is this how it should be? (Just wondering).
UPDATE 3:
Okay there is something very strange going on here: I set a breakpoint on the return statement, the last line of +evaluateExpression:usingVariables:, and it says
returnValue = 4.
This means, the actual problem lies in -performSampleExpression. What am I doing wrong?
UPDATE 4:
Changing #"%d", double into #"%f", double helped a lot. That explains the strange Console output. But, it solved it, because I updated the display like display [setText:#"%d", result]; what caused my double to be displayed as 0 due to a wrong cast.
I discovered this by using the debugger, so the one who suggested that practically solved it. (And I asked for hints, not for solutions. After all, homework is to learn from).

The possible issues can shown in:
returnValue = [worker performOperation:term];
and
returnValue = worker.operand;
You should stop debugger in this line (or line below) and watch how to your value of returnValue change.
You stop app clicking on position where it is on the screen, app stops, and then jump to next line with F6 (or Fn+F6). Below (in console) you can see the value.
EDIT
You could try this solution - Change allocation to:
CalculatorBrain *worker = [[[CalculatorBrain alloc] init] autorelease];
And delete line:
[worker release];
This release right before return may couse the problem without NSCopying #protocol.
Autorelease and NSCopying
For better understanding compile this code:
NSMutableArray *arrOne = [[NSMutableArray alloc] initWithObjects:#"1",#"2", nil];
NSMutableArray *arrTwo = [[NSMutableArray alloc] init];
arrTwo = arrOne;
[arrOne addObject:#"3"]; //After that arrTwo "shold" be 1,2 and arrOne 1,2,3. No! There both 1,2,3!
for(int i=0;i<[arrTwo count];i++)
NSLog(#"%#",[arrTwo objectAtIndex:i]);
So you may consider how you assign a variable. In above example releasing memory shouldn't effect return values, but by using autorelease with return object you don't have to assign anything.

Have you tried stepping through the code a line at a time and examining the variable values?

I'm seeing a little memory leak in your code:
so, instead of this:
[worker release];
try this line: [worker autorelease];
I'm not sure it caused the problem but your version definitely does not look good.

Related

how to test object equality using xcttest?

My method returns a NSNumber* and I want to unit test this method. Since my actual return value is NSNumber*, I create a new expected value of NSNumber*, but it fails. Here is the code:
NSNumber *cRating = [movie getRating:ratingDictionary ratingType:criticRating];
XCTAssertEqualObjects(cRating, [[NSNumber alloc]initWithInt:70], #"");
The error is:
[SFModelTest testGetCriticRatingMethod] failed: ((cRating) equal to ([[NSNumber alloc]initWithInt:70])) failed: ("70") is not equal to ("70")
Since it is saying "70" is not equal to "70", I am guessing it has to do with alloc init. Some pointer stuff that is not equal. Can somebody please help? Thank you.
Edit for comment: adding getRating method
- (NSNumber *)getRating:(NSDictionary *)movieDic ratingType:(enum RatingsEnum) rating{
NSNumber *result = 0;
NSNumber *ratingNum = 0;
switch (rating) {
case userRating:
{
ratingNum = [movieDic objectForKey:#"audience_score"];
break;
}
case criticRating:
{
ratingNum = [movieDic objectForKey:#"critics_score"];
break;
}
default:
break;
}
if(ratingNum && ratingNum > 0)
{
result = ratingNum;
}
return result;
}
The method returns NSNumber. And my test is:
NSNumber *cRating = [movie getRating:ratingDictionary ratingType:criticRating];
XCTAssertEqualObjects(cRating, [[NSNumber alloc]initWithInt:70], #"");
When I do a class NSLOG, it returns __NSCFConstantString. I am confused now.
I think that you're doing something wrong because XCTAssertEqualObjects(#(1), #(1), #"Not equal."); or XCTAssertEqualObjects([NSNumber numberWithInt:1], [NSNumber numberWithInt:1], #"Not equal."); if you are not familiar with literals, will pass.
You should checkout the getRating:ratingType: method to see which type of object it returns.
Try adding a breakpoint just before that XCTAssertEqualObjects and inspect the cRating instance.

Variadic function without nil termination

I am trying to create a method like the following:
- (void)setCondition:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
But since I'm not great with preprocessor, I hit an issue that I have fixed in the following code snippet, but I'd like to know if there's not cleaner way to achieve what I want which is to stop after the provided arguments
+ (CRCondition *)conditionWithFormat:(NSString *)format,... {
CRCondition *condition = [[CRCondition alloc] init];
NSArray *conditionSliced = [condition sliceFormatOperationFromString:format];
condition->_leftOperand = [[conditionSliced objectAtIndex:0] retain];
condition->_operator = [condition operatorFromString:[conditionSliced objectAtIndex:1]];
condition->_rightOperand = [[conditionSliced objectAtIndex:2] retain];
id eachObject;
va_list argumentList;
va_start(argumentList, format);
while ((eachObject = va_arg(argumentList, id))) {
if ([condition->_leftOperand isEqualToString:#"%K"]) {
[condition->_leftOperand release];
if ([eachObject isKindOfClass:[NSString class]])
condition->_leftOperand = [eachObject retain];
else
condition->_leftOperand = [[eachObject description] retain];
}
else if ([condition->_rightOperand isKindOfClass:[NSString class]] &&
[condition->_rightOperand isEqualToString:#"%#"]) {
[condition->_rightOperand release];
condition->_rightOperand = [eachObject retain];
}
else
break;
}
va_end(argumentList);
if (![condition isOperatorValid]) {
NSException *exception = [NSException exceptionWithName:#"Invalid Condition Operator"
reason:#"The operator passed is invalid. Must follow the following regex pattern: ([(=><)|(A-Z)]{1,2})"
userInfo:nil];
[exception raise];
}
return [condition autorelease];
}
The problem is with the while loop that circles and go past the provided arguments (I'm aware of why it's providing me other value, cmd args and such)
If you need any more explanation please add comments so I can get back to you.
The usual approach would be to parse the format string first and figure out how many arguments should follow it based on that (there is usually exactly one valid number of arguments for any format string). If the number of arguments is not deducible from a format string, it is common to terminate the list with nil (as in arrayWithObjects:...).

Program crash, need help to find the problem at least where to look

I am getting the following messages before crash:
2011-01-02 00:55:15.935 XXXX[7981:207] answerButton1
2011-01-02 00:55:15.938 XXXX[7981:207] >>>>>>>>>>>>>>>>>>>>nrQPlayer: 2
2011-01-02 00:55:15.939 XXXX[7981:207] =========whatPlayerCount===========
2011-01-02 00:55:15.939 XXXX[7981:207] ==whatPlayerCount== 1
2011-01-02 00:55:15.940 XXXX[7981:207] =========Spelare 1===========
2011-01-02 00:55:15.940 XXXX[7981:207] oooooooEND OF PLAYER!oooooooooo
2011-01-02 00:55:15.941 XXXX[7981:207] ooooooooooBEFORE IFooooooooooo
2011-01-02 00:55:15.942 XXXX[7981:207] INIT 0x5b9be30
2011-01-02 00:55:16.563 XXXX to be able to fix it[7981:207] *** -[ErrorMessage respondsToSelector:]: message sent to deallocated instance 0xca25ff0
I have been trying to track down exactly where the problem are, tried to test 'retain' in some places but somewhat running out of options now. When i try to run debugger with breakpoint but it get stuck and i cannot step forward.
I would appreciate any help possible. I am pretty new at this also, which doesn't make the situation better :-)
Here is the the part of the code that crash:
case 2: // Two players
//nrQPlayer antal spelare
NSLog(#"=========whatPlayerCount===========");
NSLog(#"==whatPlayerCount== %i", whatPlayerCount);
switch (whatPlayerCount) {
case 1:
NSLog(#"=========Spelare 1===========");
playerDiff = 1;
whatPlayerCount = 2;
thePlayer = 0;
NSLog(#"oooooooEND OF PLAYER!oooooooooo");
break;
case 2:
NSLog(#"=========Spelare 2===========");
playerDiff = 3;
whatPlayerCount = 1;
thePlayer = 2;
break;
default:
NSLog(#"=========break===========");
break;
}
NSLog(#"ooooooooooBEFORE IFooooooooooo");
NSLog(#"INIT %p", self);
// >>>>>>>>HERE IS WHERE THE CRASH HAPPENS<<<<<<<<<<
if (askedQuestions < nrOfQuestionsPerPlayer) {
NSLog(#"1");
if ([[finalPlayersInGame objectAtIndex:playerDiff] intValue] == 1) { // HARD
NSLog(#"HARD 1");
questionNr = [[hardQArray objectAtIndex:askedQuestions] intValue];
qArray = [readQuestionFunction readQuestion: questionNr];
question_TextView.text = [qArray objectAtIndex:0];
NSLog(#"HARD - qNr: %i", questionNr);
}
else if ([[finalPlayersInGame objectAtIndex:playerDiff] intValue] == 2) { // MEDIUM
NSLog(#"2");
questionNr = [[mediumQArray objectAtIndex:askedQuestions] intValue];
qArray = [readQuestionFunction readQuestion: questionNr];
question_TextView.text = [qArray objectAtIndex:0];
NSLog(#"MEDIUM - qNr: %i", questionNr);
}
else if ([[finalPlayersInGame objectAtIndex:playerDiff] intValue] == 3) { // EASY
NSLog(#"3");
questionNr = [[easyQArray objectAtIndex:askedQuestions] intValue];
qArray = [readQuestionFunction readQuestion: questionNr];
NSLog(#"qArray: %#", qArray);
NSLog(#"questionNr: %i", questionNr);
question_TextView.text = [qArray objectAtIndex:0];
NSLog(#"EASY - qNr: %i", questionNr);
}
NSLog(#"ooooooooooAFTER IFooooooooooo");
NSLog(#"4");
playerName_Label.text = [NSString stringWithFormat:#"Spelare: %#", [finalPlayersInGame objectAtIndex:thePlayer]];
playerResult_Label.text = [NSString stringWithFormat:#"Fråga %i av %i", askedQuestions, nrOfQuestionsPerPlayer];
//========CALL AccesQuestionDB MODULE TO SHUFFLE PLAYERS=========//
AccessQuestionsDB *shufflePlayersFunction = [AccessQuestionsDB new];
buttonOrder = [[NSMutableArray alloc] initWithObjects:#"1", #"2", #"3", nil];
buttonOrder = [shufflePlayersFunction shufflePlayers: buttonOrder]; // Use shufflePlayers to shuffle button also
NSLog(#"buttonOrder: %#", buttonOrder);
[shufflePlayersFunction release];
NSLog(#"5");
//========CALL buttonsOrder=========//
ButtonOrderAccess *buttonOrderFunction = [ButtonOrderAccess new];
[buttonOrderFunction saveButtonOrder: buttonOrder];
[buttonOrderFunction release];
NSLog(#"qArray: %#", qArray);
NSLog(#"buttonOrder: %#", buttonOrder);
[self.answerButton1 setTitle:[qArray objectAtIndex:[[buttonOrder objectAtIndex:0]intValue]] forState:UIControlStateNormal];
[self.answerButton2 setTitle:[qArray objectAtIndex:[[buttonOrder objectAtIndex:1]intValue]] forState:UIControlStateNormal];
[self.answerButton3 setTitle:[qArray objectAtIndex:[[buttonOrder objectAtIndex:2]intValue]] forState:UIControlStateNormal];
if (firstQuestion == YES) {
firstQuestion = NO;
//secondQuestion = YES;
}
else {
askedQuestions++;
firstQuestion = YES;
}
}
else {
// Call Error Message
ErrorMessage *callErrorMessageFunction = [ErrorMessage new];
[callErrorMessageFunction questionError: #"Q2"];
[callErrorMessageFunction release];
}
Use NSZombie, an object that catch messages sent to deallocated objects and prints the info in the console.
Refer to this question for detailed instructions:
How to run iPhone program with Zombies instrument?
We cannot know what's happening behind the screen e.g. here:
ErrorMessage *callErrorMessageFunction = [ErrorMessage new];
[callErrorMessageFunction questionError: #"Q2"];
[callErrorMessageFunction release];
but it might very well be that something in this ErrorMessage object should live on a little longer.
To fix that, don't release, but autorelease. Upon return to the runloop the object will live on, which may be long enough for your (unknown) purposes.
The typical sequence would then be:
ErrorMessage *callErrorMessageFunction = [[[ErrorMessage alloc] init] autorelease];
[callErrorMessageFunction questionError: #"Q2"];
(I prefer alloc + init over new, but they are equivalent)
You're either releasing something you shouldn't release, or not retaining an autoreleased object that you need to keep around. Go read the memory management docs. We're not seeing enough of your code here to know which it is.

Add a (double) variable to NSMutableArray

I have a user input variable and i want to add that to an array. The firstStore is a BOOL type t determine if the array has been initialize. So the first time the STORE was called, it would initialize the array. I tried to make num equal to the operand (which is a double) by masking it NSNumber but that doesn't seem to work because i have this error "NSNumber may not respond to +operand", also the program crashes when it hits the line [memArray addObject:num]. I'm new at this stuff so any help would be greatly appreciated.
else if ([operation isEqual:#"Store"]) {
if(!firstStore){
memArray = [[NSMutableArray alloc] init];
NSNumber *num = [NSNumber operand];
[memArray addObject:num];
firstStore = YES;
} else {
//NSNumber *num = [NSNumber operand];
//[memArray addObject:num];
}
}
Try changing [NSNumber operand] to [NSNumber numberWithDouble:operand].
"NSNumber may not respond to +operand" means what is says: There is no such class-method operand. What was it supposed to do?
You dont need a bool to track if memArray is initialized. use:
if(!memArray)
or
if (memArray == nil)

Objective-C Condtions operators weirdness

Okay here's the damned thing:
- (void)setMinimumNumberOfSides:(NSNumber *)newMinimumNumberOfSides {
if (newMinimumNumberOfSides != minimumNumberOfSides) {
NSNumber *minimum = [[NSNumber alloc] initWithInt:(int)2];
if (newMinimumNumberOfSides > minimum) {
[newMinimumNumberOfSides retain];
[minimumNumberOfSides release];
minimumNumberOfSides = newMinimumNumberOfSides;
} else {
NSLog(#"setMinimumNumberOfSides: Invalid number of sides: %# is smaller than the minimum of %# allowed.",
newMinimumNumberOfSides, minimum);
}
[minimum release];
[newMinimumNumberOfSides release];
}
}
There's something weird going on in there! The problem is my if (newMinimumNumberOfSides > minimum) {} condition. Even if newMinimumNumberOfSides is greated than minimum it goes into the else statement.
I did:
NSNumber *minimum = [[NSNumber alloc] initWithInt:(int)6];
[polygon setMinimumNumberOfSides:minimum];
which is way greater than 2. And I receive my error message...
I tried to NSLog those two, and it gives me the right numbers... So what's going on with this?
Thanks a lot!
I think you need to change your if statement to:
if ([newMinimumNumberOfSides intValue] > [minimum intValue])
NSNumber is an object, so you have to get its integer value before you can start using it in comparisons.
You need to use the following method to compare NSNumber objects:
- (NSComparisonResult)compare:(NSNumber *)aNumber
but better yet just convert them to c ints: [myNSNumberValue inValue]