App not performing calculation

Discussion in 'iOS Programming' started by gotenks05, Nov 1, 2011.

  1. gotenks05, Nov 1, 2011
    Last edited: Nov 1, 2011

    gotenks05 macrumors member

    Joined:
    Jan 1, 2009
    #1
    I am in the middle of trying to make one of my apps get set up to utilize pickers (UIPicker and UIDatePicker). However, I'm having a bit of trouble. When I run the app in the simulator, The result field does not display anything and XCode complains about Breakpoints. It is retrieving all the needed data, according to the information I get from Xcode (although I have not been something like NSLog to track it).

    Here is the viewcontroller code:

    Code:
    #import "RMD_CalculatorViewController.h"
    #import "RMD.h"
    
    @implementation RMD_CalculatorViewController
    @synthesize picker;
    @synthesize pickerArray;
    @synthesize birth;
    @synthesize bal;
    @synthesize rmd;
    @synthesize year;
    @synthesize yt;
    
    - (BOOL)textFieldShouldReturn:(UITextField *)textField
    {
    	[textField resignFirstResponder];
    	return YES;
    }
    
    - (IBAction)displayYear
    {
        self.yt.hidden = NO;
        self.picker.hidden = NO;
    }
    
    - (IBAction)setYear
    {
        NSInteger row = [picker selectedRowInComponent:0];
    	NSString* choice = [pickerArray objectAtIndex:row];
        self.year.text = choice;
        self.picker.hidden = YES;
        self.yt.hidden = YES;
        
    }
    
    - (IBAction)calc:(id)sender
    {
    	NSDateFormatter* df = [[NSDateFormatter alloc] init];
    	[df setDateFormat:@"MM/dd/yyyy"];
    	RMD* retire = [[RMD alloc] init];
    	NSString* bd = self.birth.text;
        NSString* syear = self.year.text;
    	double balance = self.bal.text.doubleValue;
    	[retire setBD:[df dateFromString:bd]];
    	[retire setBal:balance];
    	[retire setYear:[syear intValue]];
    	self.rmd.text = [NSString stringWithFormat:@"%.2f", [retire rmd]];
    	[df release];
    	[retire release];
    }
    
    // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
    - (void)viewDidLoad {
    	
    	NSArray* array = [[NSArray alloc] initWithObjects:@"2011",@"2012",@"2013",@"2014",@"2015",@"2016",@"2017",@"2018",@"2019",nil];
    	self.pickerArray = array;
         self.yt.hidden = YES;
        self.picker.hidden = YES;
        self.picker.delegate = self;
        self.picker.dataSource = self;
    	[array release];
        [super viewDidLoad];
    }
    
    - (void)didReceiveMemoryWarning {
    	// Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];
    	
    	// Release any cached data, images, etc that aren't in use.
    }
    
    - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
    {
    	return 1;
    }
    
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
    {
    	return [pickerArray count];
    }
    
    - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
    {
    	return [pickerArray objectAtIndex:row];
    }
    
    - (void)viewDidUnload {
    	// Release any retained subviews of the main view.
    	// e.g. self.myOutlet = nil;
    }
    
    
    - (void)dealloc {
        [super dealloc];
    }
    
    @end
    
    here is the header file:

    Code:
    #import <UIKit/UIKit.h>
    
    @interface RMD_CalculatorViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate>
    {
    	IBOutlet UIPickerView* picker;
    	NSArray* pickerArray;
    	IBOutlet UITextField* birth;
    	IBOutlet UITextField* bal;
    	IBOutlet UITextField* rmd;
        IBOutlet UITextField* year;
        IBOutlet UIToolbar* yt;
    
    }
    
    @property (nonatomic, retain) UIPickerView* picker;
    @property (nonatomic, retain) NSArray* pickerArray;
    @property (nonatomic, retain) IBOutlet UITextField* birth;
    @property (nonatomic, retain) IBOutlet UITextField* bal;
    @property (nonatomic, retain) IBOutlet UITextField* rmd;
    @property (nonatomic, retain) IBOutlet UITextField* year;
    @property (nonatomic, retain) IBOutlet UIToolbar* yt;
    - (IBAction)calc:(id)sender;
    - (IBAction)displayYear;
    /* - (IBAction)displayBirth; */
    - (IBAction)setYear;
    /* - (IBAction)setBirth; */
    @end
    
    the two IBActions commented out have not been implemented yet, which is why they are commented out, but will eventually be dealt with.

    here is the header file of the class where the variables are passed:

    Code:
    #import <UIKit/UIKit.h>
    
    /*
    The RMD class contains methods to calculate RMD */
    @interface RMD : NSObject
    {
    	NSDate* bd;
    	double bal, rmd;
    	int year;
    }
    
    + (RMD*) ret;
    - (void)setBD:(NSDate*)b;
    - (void)setBal:(double)c;
    - (void)setYear:(int)y;
    - (int)year;
    - (NSDate*)bd;
    - (int)age;
    - (double)rmd;
    @end
    
    here is the implementation:

    Code:
    #import "RMD.h"
    #import "prevDate.h"
    @implementation RMD
    
    - (id) init
    {
    	if (self = [super init])
    	{
    		[self setBD:nil];
    		[self setBal:0];
    		[self setYear:0];
    	}
    	return self;
    }
    + (RMD*) ret
    {
    	RMD* newRMD = [[RMD alloc] init]; // create instance or RMD
    	return [newRMD autorelease]; // release object and free memory
    }	
    - (void)setBD:(NSDate*)b
    {
    	bd = b;
    }
    - (void)setBal:(double)c
    {
    	bal = c;
    }
    - (void)setYear:(int)y
    {
    	year = y;
    }
    
    - (int)year
    {
    	return year;
    }
    
    - (NSDate*)bd
    {
    	return bd;
    }
    - (int)age
    {
    	prevDate* prev = [[prevDate alloc] init];
    	[prev setYear:[self year]];
    	[prev setMonth:12];
    	[prev setDay:31];
    
    	NSTimeInterval seconds = [[prev start] timeIntervalSinceDate:[self bd]];
    
    	int days = seconds/86400;
    	int age = days/365;
    	return age;
    }
    - (double)rmd
    {
    	switch([self age])
    	{
    		case 70: rmd = bal/27.4; break;
    
    		case 71: rmd = bal/26.5; break;
    
    		case 72: rmd = bal/25.6; break;
    
    		case 73: rmd = bal/24.7; break;
    
    		case 74: rmd = bal/23.8; break;
    		case 75: rmd = bal/22.9; break;
    		case 76: rmd = bal/22.0; break;
    		case 77: rmd = bal/21.2; break;
    		case 78: rmd = bal/20.3; break;
    		case 79: rmd = bal/19.5; break;
    		case 80: rmd = bal/18.7; break;
    		case 81: rmd = bal/17.9; break;
    		case 82: rmd = bal/17.1; break;
    		case 83: rmd = bal/16.3; break;
    		case 84: rmd = bal/15.5; break;
    		case 85: rmd = bal/14.8; break;
    		case 86: rmd = bal/14.1; break;
    		case 87: rmd = bal/13.4; break;
    		case 88: rmd = bal/12.7; break;
    		case 89: rmd = bal/12.0; break;
    		case 90: rmd = bal/11.4; break;
    		case 91: rmd = bal/10.8; break;
    		case 92: rmd = bal/10.2; break;
    		case 93: rmd = bal/9.6; break;
    		case 94: rmd = bal/9.1; break;
    		case 95: rmd = bal/8.6; break;
    		case 96: rmd = bal/8.1; break;
    		case 97: rmd = bal/7.6; break;
    		case 98: rmd = bal/7.1; break;
    		case 99: rmd = bal/6.7; break;
    		case 100: rmd = bal/6.3; break;
    		case 101: rmd = bal/5.9; break;
    		case 102: rmd = bal/5.5; break;
    		case 103: rmd = bal/5.2; break;
    		case 104: rmd = bal/4.9; break;
    		case 105: rmd = bal/4.5; break;
    		case 106: rmd = bal/4.2; break;
    		case 107: rmd = bal/3.9; break;
    		case 108: rmd = bal/3.7; break;
    		case 109: rmd = bal/3.4; break;
    		case 110: rmd = bal/3.1; break;
    		case 111: rmd = bal/2.9; break;
    		case 112: rmd = bal/2.6; break;
    		case 113: rmd = bal/2.4; break;
    		case 114: rmd = bal/2.1; break;
    		
    		default:
    		if([self age] >= 115)
    		{ 
    			rmd = bal/1.9;
    		}
    		else
    		{
    			rmd = 0.0; 
    		}
    		break;
    
    	}
    	return rmd;
    }
    - (void) dealloc
    {
    	[self setBD:nil];
    	[self setBal:0];
    	[self setYear:0];
    	[super dealloc]; // free memory
    }
    @end
    
    the prevDate class takes the year originally specified in the viewcontroller and decreases by one year and applies to the date of 12/31/(previous year of what was specified). All of this worked in Desktop versions of the app and it even worked in the iPhone simulator, when the UIPickerView was part of the user interface, instead of revealed by a button. So I don't get why the results will not display now.

    highlighted code is in the ViewerController, not just the AppDelegate. Also, it even highlights in green the variable I use to retrieve the input from the textfield specifying year, which seems correct already. What is causing my application not to work?
     
  2. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #2
    What exactly is Xcode printing out about the breakpoints?
     
  3. gotenks05 thread starter macrumors member

    Joined:
    Jan 1, 2009
    #3
    I can't even figure out how to get more info about the breakpoints. the info at the bottom of the windows will not tell me anything, except that the data was received. I have Xcode 4.2, which is a drastically different interface than when I originally ported the app to Objective-C, and, likewise, iOS.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    When you say "desktop versions of the app", exactly what do you mean? A Mac app? If so, did you build it with garbage collection enabled? I'm asking because your posted code doesn't manage ownership correctly, which suggests you don't completely understand memory management, or perhaps you're relying on ARC. Or perhaps you coded it in another language. Hard to guess, really.

    Your calculation of years by dividing by 365 is wrong. You're not accounting for leap years.

    If your code worked before, maybe you should archive it somewhere and post its URL, so we can see it. You might be doing something there that helps reveal why it's failing here. Seriously, comparing working code to non-working code is one of the best ways to find why something isn't working. Right now, all anyone can do is guess.

    Also, you should post the code for the prevDate class.

    And please please please: Class Names Always Start With An Upper Case Letter. not everything is lower-case. There are guidelines and conventions for this, and there are sound reasons for those guidelines.


    I recommend that you break the problem into smaller isolated problems.

    The first problem is the basic calculation. Write a simple Mac-native Cocoa command-line tool that performs the basic calculation, and uses simple NSLog calls to print results. It should be written using manual retain/release, not garbage collection, and not ARC. Use exactly the same classes as you intend to use on iOS.

    The second problem is displaying the results of the calculation in an iOS app. Once you have classes that have been tested and are known working, then you can approach iOS again. You should be able to make things work simply by performing the exact same calculation as you did in the Cocoa command-line tool. You just convert the result to a string and set the text property of a label. If the label doesn't change, then try something obvious, like setting it to a simple integer counter. If the label changes but the calculated result is wrong, then you somehow broke something in your previously working classes (the ones that were tested in the command-line tool).

    Right now, there are too many things going on that are either suspect or flat wrong. You need to isolate the problems in order to solve them. If you can't systematically isolate the problems, or even identify where they are, then it's very unlikely you'll be able to fix them.
     
  5. gotenks05, Nov 2, 2011
    Last edited: Nov 2, 2011

    gotenks05 thread starter macrumors member

    Joined:
    Jan 1, 2009
    #5
    I do the first part of class names lowercase due to the web/file compatibility I have tried to keep, so it has become habit.

    here is the prevDate header:

    Code:
    #import <UIKit/UIKit.h>
    /*
    The predate class contains methods to get the previous year */
    @interface prevDate : NSObject
    {
    	NSDate* start; // variable to hold calendar data
    	int year, month, day; // variables to hold date informate
    }
    
    + (prevDate*) prevDate;
    
    // the following are setter method, to set Date
    - (void)setYear:(int)y;
    - (void)setMonth:(int)m;
    - (void)setDay:(int)d;
    // the following are accessor methods to retrieve data
    - (int)prevYear;
    - (int)month;
    - (int)day;
    - (NSDate*)start;
    @end
    
    here is the implementation

    Code:
    #import "prevDate.h"
    @implementation prevDate
    
    - (id) init
    {
    	if (self = [super init])
    	{
    		[self setYear:0]; /* set year to zero */
    		[self setMonth:0]; /* set month */
    		[self setDay:0]; /* set day */
    	}
    	return self;
    }
    + (prevDate*) prevDate
    {
    	prevDate* newDate = [[prevDate alloc] init]; /* create instance of class */
    	return [newDate autorelease]; /* return newDate object and free memory */
    }
    
    /* setter methods */
    // the setYear method assigns a value to the year variable
    - (void)setYear:(int)y
    {
    	year = y;
    }
    // the setMonth method assigns a value to the month variable
    - (void)setMonth:(int)m
    {
    	month = m;
    }
    // the setDay method assigns a value to the day variable
    - (void)setDay:(int)d
    {
    	day = d;
    }
    
    /* getter methods */
    - (int)prevYear
    {
    	return year -1;
    }
    - (int)month
    {
    	return month;
    }
    - (int)day
    {
    	return day;
    }
    - (NSDate*)start
    {
    
    	NSCalendar* cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    	NSDateComponents* date = [[NSDateComponents alloc] init];
    	[date setYear:[self prevYear]];
    	[date setMonth:[self month]];
    	[date setDay:[self day]];
    
    	start = [cal dateFromComponents:date];
    	return start;
    	[date release];
    	[cal release];
    }
    
    - (void) dealloc
    {
    	[self setYear:0];
    	[self setMonth:0];
    	[self setDay:0];
    	
    	[super dealloc]; // free memory
    }
    @end
    
    All the code for the RMD and prevDate classes have been unmodified, since the time it was originally ported from Java to Objective-C for a mac native Desktop version, instead of a J2SE version. And those classes have been tested with an Objective-C Desktop version, as almost everything has been about the logic was tested on the Java commandline program.

    As for your complaint about the "365", how do you know if the result from the difference between the dates is exactly going to come out rendered with a year divisible by 4 (normal year) or 400 (first year of a century)? I can certainly do a modulus conditional, like you are suggesting, but there's no point when you don't exactly know what kind of date the difference will result with, and it just calculates age from the seconds.

    As for isolating, I have tried by figuring out where things are going, but Xcode is giving little help outside of claiming a breakpoint in thread 1 and highlighting stuff in green (which I already stated I could not find out more details about, especially since I have not fully grasped 4.2, coming from 3.2.x), after I click a buton.

    The picker sets the year (which indeed does get set, otherwise the year would not have been passed, just as Xcode stated it did, along with balance and birth date), or it can be manually specificed, the birth date, as can be seen in the code is formatted as "MM/dd/yyyy". The error happens when I click the button that performs the action.

    Code:
    - (IBAction)calc:(id)sender;
    
    This is all I can wire it down to. I do have backups of only functional code, but aside from moving stuff around to accommodate a hidden picker and a hidden UIToolbar (which were added), nothing much really changed. Also, I have another program, in which I did move the picker code to accommodate hiding it and the UIToolbar, but that did not give me grief like this one has, as its runtime error was simpler to fix.

    Update: I ran the code again and it said
    while it highlighted the variable that retrieves the year from UITextField in green. However, it still does not help bring it down any further.
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    First, it's unclear whether you're just learning to use the debugger, or whether you already know how (or think you do), and the debugger is misbehaving.

    If you don't know how to use the debugger, i.e. you're just learning it, then you're going to be better off learning it on a program that works. Any program will do, as long as it works. I suggest getting one of Apple's sample apps, preferably one somewhat relevant to pickers. Learn to use the debugger on that, i.e. setting breakpoints, stepping, inspecting variables, etc.

    If you don't have tutorial materials for the debugger, then you need to consult reference docs. Google search terms:
    xcode debugger site:developer.apple.com

    This does a site-specific search for xcode debugger.


    Second, your code has multiple errors in memory management. You're leaking objects. You also fail to claim ownership of objects stored in ivars.

    The leaks probably won't directly cause malfunctions. They'll just consume memory and eventually lead to memory exhaustion.

    Failure to claim ownership WILL cause malfunctions. It's entirely possible that this is the cause of your code not working. In particular, you have some ivars which are assigned an auto-released object, then that ivar is used later, even though there is no guarantee the object is still intact, or even the same type of object.

    If you haven't done an Analyze pass on your code, you ought to. It may show the leaks and the ownership failures.

    Some leaks:
    prevDate.m:
    Code:
    - (NSDate*)start
    {
    
    	NSCalendar* cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    	NSDateComponents* date = [[NSDateComponents alloc] init];
    	[date setYear:[self prevYear]];
    	[date setMonth:[self month]];
    	[date setDay:[self day]];
    
    	start = [cal dateFromComponents:date];
    [COLOR="Red"]	return start;
    [/COLOR]	[date release];
    	[cal release];
    }
    
    Really? Do you know that no code executes after a return?

    RMD.m:
    Code:
    - (int)age
    {
    [COLOR="red"]	prevDate* prev = [[prevDate alloc] init];
    [/COLOR]	[prev setYear:[self year]];
    	[prev setMonth:12];
    	[prev setDay:31];
    
    	NSTimeInterval seconds = [[prev start] timeIntervalSinceDate:[self bd]];
    
    	int days = seconds/86400;
    	int age = days/365;
    	return age;
    }
    
    The prev object isn't released or autoreleased.

    These are just a couple of leaks I noticed. There may be others.


    The number of errors involving memory management suggest you don't fully understand the memory management rules. It also suggests that a probable cause for the visible problem is related to memory management.

    Run your app under Instruments, use the allocation tool, and enable zombies. Again, you should consult a reference doc on how to do this.


    Finally, if you still can't figure out how to use the debugger, then at least use NSLog at relevant places, so you have some way to see the details of what's actually happening.
     
  7. gotenks05 thread starter macrumors member

    Joined:
    Jan 1, 2009
    #7
    I moved things around, like you were pointing out and made a release statement, where you specified, but nothing changed.

    It does not help that Xcode drastically changed things from 3.2.x, which my book covers, which I used to help port this to Objective-C, to where I have to launch console separately, when using 4.2, in order to receive NSLog statements. Back in 3.2.x, I just had to issue CMD+Shift+R and I can get NSLog statements, which is how I got things to work in the desktop version.

    Anyway, I put in NSLog to retrieve some data and Console, which I will remind you I had to launch separately and nothing came up like it did in the variable list in the debugger area. I did run the analyze option though.

    All I can say is that the changes to Xcode are as bad or even worse that MS Office 2007 from previous versions.
     
  8. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #8
    (yawn) I'm really board with this whine (both of those whines, actually).

    If you understand what is your actually trying to do as opposed to following along blindly with click-here type-that instructions, the transition from XCode 3 and XCode 4 is not hard.

    XCode 4 Transition Guide.
     
  9. gotenks05 thread starter macrumors member

    Joined:
    Jan 1, 2009
    #9
    Well, sorry! Anyway, the analyze feature helped out more than you did, except that I will thank you for your help, as it may have been a bit worse results. The real problem was the fact that prevDate was not released properly in the RMD class.

    Thanks for links though.
     
  10. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #10
    Frankly, that seems unlikely, at least for the posted code:
    Code:
    - (int)age
    {
    	prevDate* prev = [[prevDate alloc] init];
    	[prev setYear:[self year]];
    	[prev setMonth:12];
    	[prev setDay:31];
    
    	NSTimeInterval seconds = [[prev start] timeIntervalSinceDate:[self bd]];
    
    	int days = seconds/86400;
    	int age = days/365;
    	return age;
    }
    
    That's the only code in RMD's implementation that even references the prevDate class.

    As I previously noted, the prevDate being created is not being released, but that's a simple memory leak.

    The lifetime and the scope of the local variable ends when the method returns. That means there are no other references to the created object, which is why it's a leak. If there are no references to an object, then there's no way that object can influence anything, unless that instance can affect other variables with global or static lifetime. The prevDate class has no global or static variables, so that pathway is closed. So unless there's some other bug involving multiple prevDate instances, I don't see how a leaked prevData can possibly cause a problem (other than memory leakage).

    However, the [self bd] reference returns an ivar that hasn't been properly retained in its setter method. That's one of the memory management bugs that hasn't been specifically mentioned, but which can easily have unpredictable consequences if the object assigned to the ivar is autoreleased.
     

Share This Page