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.
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.
Related
I'm relatively new to Objective-C, so I'm not 100% about everything I'm coding. However, I'm tackling my errors as they happen, and I am getting a run-time error that I'm not sure how to fix. The error claims that an 'unrecognized selector [was] sent to instance.'
- (IBAction) equalsPressed{
self.userIsInTheMiddleOfTypingANumber = NO;
if (self.brain.operationIsPicked) {
[self.brain pushOperand: [self.display.text doubleValue]];
double result = [self.brain performOperation: self.brain.operation];
// The line below this
self.display.text = [NSString stringWithFormat: #"%g", result];
}
}
I'm doing the Calculator app taught by the Stanford professor whose course is posted in iTunes U. However, I edited it to not include an enterPressed command and instead use an altered and more user-friendly 'equalsPressed.' After equalsPressed is finished (meaning when I click the equals sign on the calculator), the error is displayed. Might anyone know what is causing this problem? I already found someone else who has an error very similar to this one, but occurring in a slightly different place. Here are the other code snippets that could help elucidate the problem.
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
#implementation CalculatorBrain
#synthesize operandStack = _operandStack;
#synthesize operandStackIsEmpty = _operandStackIsEmpty;
#synthesize operationIsPicked = _operationIsPicked;
#synthesize operation = _operation;
- (NSMutableArray *) operandStack {
if (!_operandStack) {
_operandStack =[[NSMutableArray alloc] init];
_operandStackIsEmpty = YES;
_operationIsPicked = NO;
}
return _operandStack;
}
- (void) resetStack {
[self.operandStack removeAllObjects];
self.operandStackIsEmpty = YES;
self.operationIsPicked = NO;
}
- (void) pushOperand : (double) operand{
[self.operandStack addObject: [NSNumber numberWithDouble:operand]];
self.operandStackIsEmpty = NO;
}
- (double) popOperand {
NSNumber *num = [self.operandStack lastObject];
if (num) {[self.operandStack removeLastObject];}
return [num doubleValue];
}
- (double) performOperation : (NSString *) operation{
double result = 0;
double num2 = [self popOperand];
double num1 = [self popOperand];
if ([operation isEqualToString:#"+"])
result = num2 + num1;
else if ([operation isEqualToString:#"-"])
result = num2 - num1;
else if ([operation isEqualToString:#"*"] || [operation isEqualToString:#"x"])
result = num2 * num1;
else if ([operation isEqualToString:#"/"]){
if (num2 == 0)
[self resetStack];
else
result = num2 / num1;
}
[self pushOperand:result];
self.operationIsPicked = NO;
return result;
}
#end
Also, the debugging NSLog that I had print the display in the console works correctly, BUT the display in the Calculator view doesn't actually update (when I used breakpoints to slow the function down).
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic) BOOL userIsInTheMiddleOfTypingANumber;
#property (nonatomic, strong) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize userIsInTheMiddleOfTypingANumber = _userIsInTheMiddleOfTypingANumber;
#synthesize brain = _brain;
#synthesize display = _display;
- (CalculatorBrain *) brain {
if (!_brain) {
_brain =[[CalculatorBrain alloc] init];
}
return _brain;
}
- (IBAction) digitPressed: (UIButton *)sender {
NSString *digit = [sender currentTitle];
if (self.userIsInTheMiddleOfTypingANumber)
self.display.text = [self.display.text stringByAppendingString: digit];
else {
self.display.text = digit;
self.userIsInTheMiddleOfTypingANumber = YES;
}
}
- (IBAction) clearPressed {
self.userIsInTheMiddleOfTypingANumber = NO;
self.display.text = #"0";
[self.brain resetStack];
}
- (IBAction) equalsPressed {
self.userIsInTheMiddleOfTypingANumber = NO;
if (self.brain.operationIsPicked) {
[self.brain pushOperand: [self.display.text doubleValue]];
double result = [self.brain performOperation: self.brain.operation];
self.display.text = [NSString stringWithFormat: #"%g", result];
NSLog(#"%#",self.display.text);
}
NSLog(#"%#",self.display.text);
}
- (IBAction) operationPressed:(UIButton *) sender {
if (self.userIsInTheMiddleOfTypingANumber) {
[self.brain pushOperand: [self.display.text doubleValue]];
self.brain.operation = [sender currentTitle];
self.brain.operationIsPicked = YES;
self.userIsInTheMiddleOfTypingANumber = NO;
NSLog(#"%#", sender.currentTitle);
}
}
Thank you!
To quote the answer in the comments:
"The full error said that I sent an invalid argument to "[self.brain equalsPressed:]". In reality, equalsPressed takes no argument, but when I was linking the '=' button in my view to its respective method, I forgot to indicate that the function took no arguments. Hence even though the function as I wrote it has no arguments, I told the compiler (to begin with) that it was supposed to take an argument."
For a school assignment I have been told to make a calculator app, the same as the spotlight calculator. It works in realtime and has no buttons for things to begin.
So far this is my code. It is written in a text field with the event Editing Did End. Im pretty sure thats wrong but i can't find an alternative solution. Also i haven't gotten the realtime thing to work so i've kind of reverted to completing the following steps when pressed off the text field.
- (IBAction)Didend_Action:(id)sender {
NSString *list = [Sum_TextField text];
NSArray *listItemsArray = [list componentsSeparatedByString:#" "];
float firstNumber = [[listItemsArray objectAtIndex: 0] floatValue];
NSString *symbol = [listItemsArray objectAtIndex: 1];
float secondNumber = [[listItemsArray objectAtIndex: 2] floatValue];
{
Calculator* calc = [[Calculator alloc] init];
[calc setNum1:firstNumber];
[calc setNum2:secondNumber];
if ([symbol isEqualToString:#"-"])
{
[calc minus];
}
else if ([symbol isEqualToString:#"+"])
{
[calc add];
}
if ([symbol isEqualToString:#"*"])
{
[calc multiply];
}
else if ([symbol isEqualToString:#"/"])
{
[calc divide];
}
[Answer_TextField setText:[NSString stringWithFormat:#"%d", [calc answer]]];
}
}
I think a better way to do it would be to implement the UITextViewDelegate protocol methods like textViewDidChange:. For example, you could do something like this:
- (void)textViewDidChange:(UITextView *)textView {
NSString *currentText = [textview text];
NSArray *currentItems = [currentText componenetsSeparatedByString:#" "];
float result = 0.0;
//If a valid expression is in the text view
if([currentItems count] > 2) {
float num1 = [[currentItems objectAtIndex:0] floatValue];
float num2 = [[currentItems objectAtIndex:2] floatValue];
NSString *operator = [currentItems objectAtIndex:1];
if([operator isEqualToString:#"+"]) {
result = num1 + num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else if([operator isEqualToString:#"-"]) {
result = num1 - num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else if([operator isEqualToString:#"*"]) {
result = num1 * num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else if([operator isEqualToString:#"/"]) {
result = num1 / num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else{
answerTextField.text = #"Invalid Operation";
}
}
}
This would be called every time the user edited the text in the text view. It should work, but I didn't test it out. Make sure that in the header of whatever file this code is in, you do this:
#interface yourClassName : yourSuperclass <UITextViewDelegate> {
//Your instance variables
}
//Your method and property declarations
EDIT:
Let's say I put the - (void)textViewDidChange:(UITextView *)textView code in a file called MyClass.m. The file MyClass.m would then look like this:
#implementation MyClass
- (void)textViewDidChange:(UITextView *)textView {
//All the above code goes here
}
- (void)viewDidLoad
{
[super viewDidLoad];
//INCLUDE THESE LINES
Sum_TextField.delegate = self;
Answer_TextField.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
In the header file (MyClass.h), I would put this:
#interface MyClass : UIViewController <UITextViewDelegate>
//Note: you don't declare - (void)textViewDidChange:(UITextView *)textView in the header file because you are implementing
//a protocol method.
//MAKE SURE SUM_TEXTFIELD AND ANSWER_TEXTFIELD ARE UITEXTVIEWS NOT UITEXTFIELDS
#property (strong, nonatomic) IBOutlet UITextView *Sum_TextField;
#property (strong, nonatomic) IBOutlet UITextView *Answer_TextField;
#end
Hope this helps!
I am 100% brand new to iOS.
I am watching Stanford U's videos online to learn iOS. I spent hours slowly going through the second video meticulously ensuring I didn't goof on a line of code. Everything was perfect until - Wouldn't ya know it - the LAST minute of coding.
My wife and I spent a good amount of time trying to figure it out but my code matches the professor's as best we can tell. Please help me. I am loving learning through these videos but I can't move on until I have this functioning and take time to review it.
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController()
#property (nonatomic) BOOL userIsInTheMiddleOfEnteringANumber;
#property (nonatomic, strong) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize display = _display;
#synthesize userIsInTheMiddleOfEnteringANumber =
_userIsInTheMiddleOfEnteringANumber;
#synthesize brain = _brain;
- (CalculatorBrain *)brain
{
if (!_brain) _brain = [[CalculatorBrain alloc] init];
return _brain;
}
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = sender.currentTitle;
if (self.userIsInTheMiddleOfEnteringANumber)
{
self.display.text = [self.display.text stringByAppendingString:digit];
}
else
{
self.display.text = digit;
self.userIsInTheMiddleOfEnteringANumber = YES;
}
}
- (IBAction)enterPressed
{
[self.brain pushOperand:[self.display.text doubleValue]];
self.userIsInTheMiddleOfEnteringANumber = NO;
}
- (IBAction)operationPressed:(id)sender
{
if (self.userIsInTheMiddleOfEnteringANumber) [self enterPressed];
double result = [self.brain performOperation:sender.currentTitle]; // <- HERE IS THE LINE IN QUESTION
NSString *resultString = [NSString stringWithFormat:#"%g", result];
self.display.text = resultString;
}
#end
CalculatorBrain.m
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
#implementation CalculatorBrain
#synthesize operandStack = _operandStack;
- (NSMutableArray *) operandStack
{
if (_operandStack == nil) _operandStack = [[NSMutableArray alloc] init];
return _operandStack;
}
- (void)pushOperand:(double)operand
{
[self.operandStack addObject:[NSNumber numberWithDouble:operand]];
}
- (double)popOperand
{
NSNumber *operandObject = [self.operandStack lastObject];
if (operandObject) [self.operandStack removeLastObject];
return [operandObject doubleValue];
}
- (double)performOperation:(NSString *)operation
{
double result = 0;
if ([operation isEqualToString:#"+"])
{
result = [self popOperand] + [self popOperand];
}
else if ([#"*" isEqualToString:operation])
{
result = [self popOperand] * [self popOperand];
}
[self pushOperand:result];
return result;
}
#end
I think the problem is with your if statement not closing properly.
- (IBAction)operationPressed:(id)sender
{
if (self.userIsInTheMiddleOfEnteringANumber){ [self enterPressed];
double result = [self.brain performOperation:sender.currentTitle];
NSString *resultString = [NSString stringWithFormat:#"%g", result];
self.display.text = resultString;
}
}
or
- (IBAction)operationPressed:(id)sender
{
if (self.userIsInTheMiddleOfEnteringANumber)
{
[self enterPressed];
}
double result = [self.brain performOperation:sender.currentTitle];
NSString *resultString = [NSString stringWithFormat:#"%g", result];
self.display.text = resultString;
}
Suggest looking at the pdf file of the tutorial for a more concrete code source. Hope that help.
I want to access the property storedValue from the class method popOperandOffStack:, but I get an error. I read that you cannot access storedValue because it is a class method or something like that. Can anyone tell me how can I access storedValue or how can I make storedValue global so that I can use it in popOperandOffStack:?
Here is my header:
#import <Foundation/Foundation.h>
#interface CalculatorBrain : NSObject
- (void)pushOperand:(double)operand; // - = instance method
- (double)performOperation: (NSString *)operation;
#property (nonatomic,readonly) id program;
+ (double)runProgram:(id)program; // + = class method
+ (NSString *)descriptionOfProgram:(id)program;
#end
And this is my m file:
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic,strong) NSMutableArray *programStack;
#property (nonatomic) double storedValue;
#end
#implementation CalculatorBrain
#synthesize programStack = _programStack;
#synthesize storedValue = _storedValue;
- (NSMutableArray *)programStack
{
if (_programStack == nil)
_programStack = [[NSMutableArray alloc] init];
return _programStack;
}
- (void)setOperandStack:(NSMutableArray *)programStack
{
_programStack = programStack;
}
- (void)pushOperand:(double)operand
{
[self.programStack addObject:[NSNumber numberWithDouble:operand]];
}
- (double)performOperation: (NSString *)operation
{
[self.programStack addObject:operation];
return [[self class] runProgram:self.program];
}
- (id)program
{
return [self.programStack copy];
}
+ (NSString *)descriptionOfProgram:(id)program
{
return #"Implement this in Assignment 2";
}
+ (double) popOperandOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack)
[stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]])
{
result = [topOfStack doubleValue];
}
else if ([topOfStack isKindOfClass:[NSString class]])
{
NSString *operation = topOfStack;
if ([operation isEqualToString:#"+"]){
result = [self popOperandOffStack:stack] + [self popOperandOffStack:stack];
} else if ([operation isEqualToString:#"-"]){
result = -[self popOperandOffStack:stack] + [self popOperandOffStack:stack];
} else if ([operation isEqualToString:#"*"]){
result = [self popOperandOffStack:stack] * [self popOperandOffStack:stack];
} else if ([operation isEqualToString:#"/"]){
double divide = [self popOperandOffStack:stack];
if(divide){
result = [self popOperandOffStack:stack] / divide;
} else {
result = 0;
}
} else if ([operation isEqualToString:#"1/x"]){
result = 1 / [self popOperandOffStack:stack];
} else if ([operation isEqualToString:#"sqrt"]){
double sqrtNumber = [self popOperandOffStack:stack];
if (sqrtNumber > 0){
result = sqrt(sqrtNumber);
} else {
result = 0;
}
} else if ([operation isEqualToString:#"sin"]){
result = sin( [self popOperandOffStack:stack]);
} else if ([operation isEqualToString:#"cos"]){
result = cos( [self popOperandOffStack:stack]);
} else if ([operation isEqualToString:#"MS"]){
self.storedValue = [self popOperandOffStack:stack];
} else if ([operation isEqualToString:#"MR"]){
result = self.storedValue; //error here
} else if ([operation isEqualToString:#"MC"]){
result = [self popOperandOffStack:stack];
self.storedValue = 0; //error here
} else if ([operation isEqualToString:#"M+"]){
result = [self popOperandOffStack:stack];
self.storedValue = self.storedValue + result; //error here
} else if ([operation isEqualToString:#"M-"]){
result = [self popOperandOffStack:stack];
self.storedValue = self.storedValue - result; //error here
} else if ([operation isEqualToString:#"C"]){
result = 0;
while ( [self popOperandOffStack:stack] )
{
//clear operands until returns NO
}
} else if ([operation isEqualToString:#"CE"]){
result = 0;
self.storedValue = 0; //error here
while ( [self popOperandOffStack:stack] )
{
//clear operands until returns NO
}
}
if (![operation isEqualToString:#"MS"] && ![operation isEqualToString:#"CE"])
{
return result;
} else {
return self.storedValue;
}
}
return result;
}
+ (double)runProgram:(id)program
{
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
return [self popOperandOffStack:stack];
}
#end
I cannot use self.storedValue. What should I do besides changing the + (static method) to - (instance method) (I am not allowed to do that)?
You can't use instance getter/setter methods from inside a class method because you don't have an object that is an instance of CalculatorBrain. (I haven't used that course but I would hope that it explains the difference between classes and objects.)
Even though it's not a good idea from the point of view of creating an object-oriented program, you could use a static variable to hold your double. Look at a C language reference for the phrase "external static" to see examples.
I've got a leak in my application and I do not know why. Maybe I've got all memory managment thing wrong. In my code I've got UIViewController object which have ivar TelephoneValidator *validator
TelephoneValidator is TelephoneValidator : NSObject
So in my initialization function of UIViewController object (initWithFieldData) I've got:
-(id) initWithFieldData: (NSMutableDictionary*) fieldData
{
...
validatorOptions = [fieldData objectForKey:#"fieldValidator"];
...
}
Now in my viewDidLoad I've got:
- (void)viewDidLoad {
...
if (![validatorOptions respondsToSelector:#selector(isEqualToString:)]) {
validator = [[TelephoneValidator alloc] initWithOptions: validatorOptions];
}
else {
validator = nil;
}
...
}
Basicly if my validatorOptions isn't NSString the validator ivar became TelephoneValidator instance.
In my dealloc:
- (void)dealloc {
if(validator != nil)
{
[validator release];
validator = nil;
}
...
[super dealloc];
}
I've checked a couple of times if dealloc works, and it is. After calling dealloc the validator is released (calling any method on validator after [validator release] gets me exception).
And yet in Instruments it is telling me that TelephoneValidator is leaked. And after double clicking in Instruments the line of code that is highlited is:
validator = [[TelephoneValidator alloc] initWithOptions: validatorOptions];
What am I doing wrong?
UPDATE:
Here is my header information of UIViewController:
#interface GenericViewController : UIViewController <UITextFieldDelegate>{
UIImage *backgroundImage;
NSString *step; // na ktorym kroku jestesmy
id <GenericControllerDelegate> delegate; //delegata z ktorej bedziemy pobierali dane
UITextField *textField;
NSString *fieldName; //nazwa pola (potrzebujemy zeby zapisac do modelu odpowiedni tekst
UILabel *textLabel;
UILabel *stepsLabel;
UILabel *prefixTextLabel;
NSString *fieldPlaceholder;
NSString *textLabelText;
NSString *textLabelTextPl; //w jezyku polskim
NSString *prefixTextLabelText; //w jezyku eng
NSString *prefixTextLabelTextPl; //w jezyku polskim prefix
NSString *fieldRequired;
NSString *keyboardType;
NSString *capitalizeType;
UIButton *button; //forward button
UIButton *button2; //backward button
//to bedzie do przerobienia bo bedziemy mieli tablicje walidatorow a nie jeden walidator
NSString *validatorType;
//maksymalna dlugosc pola
int maxLengthOfTextField;
NSArray* validatorOptions;
TelephoneValidator *validator;
//patientModel
PatientData *patientModel;
}
TelephoneValidator header:
#import <Foundation/Foundation.h>
#import "MAOTranslate.h"
#interface TelephoneValidator : NSObject {
//opcje walidacyjne
NSString *phonePrefix;
NSString *phonePostfix;
int phoneLength;
NSString *message;
NSString *messagePl;
UIAlertView *alertView;
}
-(id) initWithOptions:(NSArray *) optionsArray;
-(void) displayMessage;
-(BOOL) validate: (NSString *) phoneNumber;
#end
TelephoneValidator class:
#import "TelephoneValidator.h"
#implementation TelephoneValidator
//#synthesize phoneNumber;
-(id) initWithOptions:(NSArray *) optionsArray;
{
if(self = [[TelephoneValidator alloc] init])
{
phonePrefix = [optionsArray objectAtIndex:0];
phonePostfix = [optionsArray objectAtIndex:1];
phoneLength = [[optionsArray objectAtIndex:2] intValue];
message = [optionsArray objectAtIndex:3];
messagePl = [optionsArray objectAtIndex:4];
}
else {
self = nil;
}
return self;
}
//wyswietlamy wiadomosc
-(void) displayMessage
{
NSString *displayMsg;
if ([[MAOTranslate getLanguage] isEqualToString:#"pl"]) {
displayMsg = messagePl;
}
else {
displayMsg = message;
}
alertView = [[UIAlertView alloc] initWithTitle:#"Alert" message:displayMsg delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alertView show];
}
-(BOOL) validate: (NSString *) phoneNumber
{
//dlugosc
if ([phoneNumber length] != phoneLength) {
NSLog(#"zla dlugosc");
return NO;
}
NSLog(#"tutaj");
//sprawdzamy prefix
if ([phonePrefix length]!= 0) {
NSLog(#"w srodku ifa");
if ([phoneNumber compare:phonePrefix options:NSLiteralSearch range:NSMakeRange(0, [phonePrefix length])] != 0) {
NSLog(#"zly prefix");
[self displayMessage];
return NO;
}
}
//sprawdzamy postfix
if([phonePostfix length] != 0)
{
if ([phoneNumber compare:phonePostfix options:NSLiteralSearch range:NSMakeRange([phoneNumber length]-[phonePostfix length], [phonePostfix length])] != 0) {
NSLog(#"zly postfix");
[self displayMessage];
return NO;
}
}
//sprawdzamy czy string jest numeryczny
NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:phoneNumber];
if (![alphaNums isSupersetOfSet:inStringSet])
{
NSLog(#"zly format ");
[self displayMessage];
return NO;
}
return YES; //zwalidowany poprawnie
}
-(void) dealloc
{
[alertView release];
alertView = nil;
[super dealloc];
}
You need to call [super dealloc] at the end of the dealloc method.
See These both lines
validator = [[TelephoneValidator alloc] initWithOptions: validatorOptions];
and inside initWithOptions
if(self = [[TelephoneValidator alloc] init])
You are allocing twice the validator, so there is a leak.
Could it be that instruments is pointing to validatorOptions as the source of the leak? Is it a retained property being released at dealloc or not? I can't say for sure, the code you posted is not enough to arrive to a conclusion.
Also, as willcodejavaforfood says, you must always call [super dealloc]; at the end of your dealloc method. No code must come after it.
Edit:
I'm back. But Bruno Domingues got it right already, you are allocating twice, in which case, the first one leaks. You should change your -initWithOptions: code to:
-(id) initWithOptions:(NSArray *) optionsArray;
{
if((self = [super init])){
// ... rest of code is fine
}
return self;
}