I've got a method that when put a breakpoint in it and hover over a string, says it's out of scope and you can drill down into the NSString object for what seems like forever. I've tried to put a screen shot... hope it shows up. I think I have some serious memory management problems...
http://web.me.com/gazelips/Site/Blank_files/screenshot.jpg
Here's the whole .h and .m files. The problem occurs in the updateAdvice method.
#import <UIKit/UIKit.h>
#import "SourcePickerViewController.h"
#import "Chemical.h"
#interface AdjustViewController : UIViewController <SourcePickerViewControllerDelegate>{
// IB controls
UITextField *sourceField;
UITextField *volumeField;
UILabel *startingLabel;
UILabel *targetLabel;
UITextView *adviceLabel;
// Setup variables for the kind of chemical
int numberOfComponents;
NSDictionary *dictionaryOfSources;
// Local ivars
float percentRemove;
float gallonsRemove;
float selectedChemSourceAmount;
int delta;
NSString *selectedChemName;
NSString *selectedChemConcentration;
float selectedChemConstant;
BOOL selectedChemIsLiquid;
NSString *compositeName;
NSString *messageBody;
NSString *adviceMessage;
}
#property (nonatomic, retain) IBOutlet UITextField *sourceField;
#property (nonatomic, retain) IBOutlet UITextField *volumeField;
#property (nonatomic, retain) IBOutlet UILabel *startingLabel;
#property (nonatomic, retain) IBOutlet UILabel *targetLabel;
#property (nonatomic, retain) IBOutlet UITextView *adviceLabel;
#property (nonatomic, retain) NSString *selectedChemName;
#property (nonatomic, retain) NSString *selectedChemConcentration;
#property float selectedChemConstant;
#property BOOL selectedChemIsLiquid;
#property (nonatomic, retain) NSString *compositeName;
#property (nonatomic, retain) NSString *messageBody;
#property (nonatomic, retain) NSString *adviceMessage;
#property int numberOfComponents;
#property (nonatomic, retain) NSDictionary *dictionaryOfSources;
- (IBAction)backgroundTap:(id)sender;
//- (IBAction)textFieldDoneEditing:(id)sender;
- (IBAction)startingSliderChanged:(id)sender;
- (IBAction)startingSliderFinishedChanging;
- (IBAction)targetSliderChanged:(id)sender;
- (IBAction)targetSliderFinishedChanging;
- (IBAction)getChemicalSource;
- (void)updateAdvice;
#end
#import "AdjustViewController.h"
#implementation AdjustViewController
#synthesize sourceField;
#synthesize volumeField;
#synthesize startingLabel;
#synthesize targetLabel;
#synthesize adviceLabel;
#synthesize numberOfComponents;
#synthesize dictionaryOfSources;
#synthesize compositeName;
#synthesize messageBody;
#synthesize adviceMessage;
#synthesize selectedChemName;
#synthesize selectedChemConcentration;
#synthesize selectedChemConstant;
#synthesize selectedChemIsLiquid;
- (IBAction)backgroundTap:(id)sender {
[sourceField resignFirstResponder];
[volumeField resignFirstResponder];
[self updateAdvice];
}
- (IBAction)startingSliderChanged:(id)sender {
UISlider *slider = (UISlider *)sender;
int progressAsInt = (int)(slider.value + 0.5f);
NSString *newValue = [[NSString alloc] initWithFormat:#"%d", progressAsInt];
startingLabel.text = newValue;
[newValue release];
}
- (IBAction)targetSliderChanged:(id)sender {
UISlider *slider = (UISlider *)sender;
int progressAsInt = (int)(slider.value + 0.5f);
NSString *newValue = [[NSString alloc] initWithFormat:#"%d", progressAsInt];
targetLabel.text = newValue;
[newValue release];
}
- (IBAction)startingSliderFinishedChanging {
// [self updateAdvice];
}
- (IBAction)targetSliderFinishedChanging {
// [self updateAdvice];
}
// Present the picker for chlorine selection
- (IBAction)getChemicalSource {
SourcePickerViewController *sourcePickerViewController = [[SourcePickerViewController alloc] init];
sourcePickerViewController.delegate = self;
NSLog(#"getChemicalSource setting numberOfComponents %d", self.numberOfComponents);
sourcePickerViewController.numberOfComponents = self.numberOfComponents;
NSLog(#"getChemicalSource sending numberOfComponents %d", sourcePickerViewController.numberOfComponents);
sourcePickerViewController.dictionaryOfSources = self.dictionaryOfSources;
[self presentModalViewController:sourcePickerViewController animated:YES];
[sourcePickerViewController release];
}
- (void)updateAdvice {
NSLog(#"--updateAdvice");
NSLog(#" selectedChemical name = %#", selectedChemName);
NSLog(#" selectedChemical concentration = %#", selectedChemConcentration);
NSLog(#" selectedChemical constant = %1.6f", selectedChemConstant);
NSLog(#" selectedChemical is liquid = %d", selectedChemIsLiquid);
// First check to see if there is a source AND volume, otherwise prompt user to enter them
if ([volumeField.text isEqualToString:#""] || [sourceField.text isEqualToString:#""]) {
adviceMessage = #"Enter a source and volume.";
}
// If there IS a source and volume, calculate!
else {
if ([selectedChemConcentration isEqualToString:#""]) { // If there's no concentration, make a string with just the name
compositeName = selectedChemName;
NSLog(#" compositeName without concentration = %#", compositeName);
}
else { // But if there is a concentration, make a string with the name and concentration and a space between.
compositeName = [[NSString alloc] initWithFormat:#"%# %#", selectedChemName, selectedChemConcentration];
NSLog(#" compositeName with concentration = %# %#", compositeName, selectedChemConcentration);
}
delta = [targetLabel.text intValue] - [startingLabel.text intValue]; // The difference between target and starting levels
NSLog(#" delta = %d", delta);
sourceAmount = delta * [volumeField.text intValue] * sourceConstant; // Calculates the amount of source chemical necessary in ounces
NSLog(#" sourceAmount = %1.1f", sourceAmount);
// If delta is positive, add chemical
if (delta > 0) {
NSLog(#">> Delta > 0");
if (selectedChemIsLiquid) {
if (sourceAmount > 128) { // Amount is more than a gallon
sourceAmount = sourceAmount / 128;
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f gal of ", self.title, delta, sourceAmount];
}
else { // Less than a gallon
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f fl oz of ", self.title, delta, sourceAmount];
}
}
else { // Chemical is a solid
if (sourceAmount > 16) { // Amount is more than a pound
sourceAmount = sourceAmount / 16;
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f lb of ", self.title, delta, sourceAmount];
}
else { // Less than a pound
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f oz of ", self.title, delta, sourceAmount];
}
}
adviceMessage = [[NSString alloc] initWithFormat:#"%#%#.", messageBody, compositeName];
}
// If delta is zero, stay the course
if (delta == 0) {
NSLog(#"== Delta = 0");
adviceMessage = #"You're on target. No action necessary.";
}
// If delta is negative, remove water
if (delta < 0) {
NSLog(#"<< Delta < 0");
adviceMessage = #"You're over target. Remove some water.";
}
}
adviceLabel.text = adviceMessage; // Set the advice label
[messageBody release]; // And get rid of message
[compositeName release];
[adviceMessage release];
}
- (void)viewDidLoad {
NSLog(#"AdjustViewController launched");
sourceField.text = #"";
adviceLabel.text = #"";
percentRemove = 0;
gallonsRemove = 0;
delta = 0;
selectedChemSourceAmount = 0;
// [self updateAdvice];
[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.
}
- (void)viewDidUnload {
sourceField = nil;
volumeField = nil;
startingLabel = nil;
targetLabel = nil;
adviceLabel = nil;
dictionaryOfSources = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[sourceField release];
[volumeField release];
[startingLabel release];
[targetLabel release];
[adviceLabel release];
[dictionaryOfSources release];
[super dealloc];
}
#pragma mark -
#pragma mark Picker View Delegate Methods
// Returns the values from the picker if a source was chosen
- (void)sourcePickerViewController:(SourcePickerViewController *)controller
didSelectSource:(NSString *)source
andConcentration:(NSString *)concentration
andConstant:(float)constant
andIsLiquid:(BOOL)isLiquid {
selectedChemName = source;
selectedChemConcentration = concentration;
selectedChemConstant = constant;
selectedChemIsLiquid = isLiquid;
// Update the source textfield. If concentration is empty, just use the source otherwise concatenate them
if ([selectedChemConcentration isEqualToString:#""]) {
sourceField.text = [[NSString alloc] initWithFormat:#"%#", selectedChemName];
}
else {
sourceField.text = [[NSString alloc] initWithFormat:#"%# %#", selectedChemName, selectedChemConcentration];
}
// [self updateAdvice];
NSLog(#"Returned source = %#, concentration = %#, constant = %1.7f, isLiquid = %d", source, concentration, constant, isLiquid);
NSLog(#"selectedChemical.chemName = %#, chemConcentration = %#, chemConstant = %1.7f, chemIsLiquid = %d", selectedChemName, selectedChemConcentration, selectedChemConstant, selectedChemIsLiquid);
[self dismissModalViewControllerAnimated:YES];
}
// Returns from the picker without choosing a new source
- (void)sourcePickerViewController:(SourcePickerViewController *)controller
didSelectCancel:(BOOL)didCancel {
// [self updateAdvice];
NSLog(#"Returned without selecting source");
[self dismissModalViewControllerAnimated:YES];
}
#end
What you're seeing there is not an infinite chain of superclasses (if you look at the actual address you'll see it does not change after the second time), you're actually looking at the NSObject metaclass. Have a quick read through that link—wonderfully provided by Matt Gallagher—and it will explain what you're seeing in the debugger. NSObject's metaclass's metaclass is itself.
This is actually quite common and not necessarily anything to do with a bug in your code.
If you right click on the string object in the debugger window and select "print description to console", it should work just fine.
I'm just looking at your code anyway. What the other two people said hold... however, memory management issues:
if (sourceAmount > 16) { // Amount is more than a pound
sourceAmount = sourceAmount / 16;
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f lb of ", self.title, delta, sourceAmount];
}
else { // Less than a pound
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f oz of ", self.title, delta, sourceAmount];
}
[...]
[messageBody release]; // And get rid of message
[compositeName release];
[adviceMessage release];
I think it would be better for you to simply write
self.messageBody = [NSString stringWithFormat:#"To increase %#...", self.title];
This automatically invokes the accessor methods which you synthesized using #synthesize messageBody.
It will automatically release the old string, and retain the new one.
What you are doing right now is allocating a new string without releasing the old string. And why do that yourself when you have already synthesized the accessor methods?
Finally, don't forget to properly release the messageBody in dealloc, where you should be releasing it.
- (void)dealloc
{
[messageBody release];
[everythingElseIHaveToRelease release];
[super dealloc];
}
Hope that helps!
H
Related
I trying to learn how to save array of objects using NSKeyedArchiver and I coded a small application to do that and I logged to see if the array was saved but everytime I get 0 for array count and here is the code.
ViewController.h
#interface ViewController : UIViewController
{
IBOutlet UITextField *text;
IBOutlet UITextField *textName;
IBOutlet UITextField *textAge;
IBOutlet UILabel *name;
IBOutlet UILabel *age;
BOOL flag;
BOOL choice;
NSString *documentDirectory;
NSMutableArray *anArray;
Person *p;
NSData *data;
}
-(BOOL) dataFilePath;
-(IBAction)readPlist;
-(IBAction) writePlist;
#property (strong,nonatomic)IBOutlet UITextField *text;
#property (strong,nonatomic)IBOutlet UITextField *textName;
#property (strong,nonatomic)IBOutlet UITextField *textAge;
#property (strong,nonatomic)IBOutlet UILabel *name;
#property (strong,nonatomic)IBOutlet UILabel *age;
#property (strong,nonatomic)NSString *documentDirectory;
#property (strong,nonatomic)NSMutableArray *anArray;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
#synthesize text,documentDirectory,textAge,textName,name,age,anArray;
- (void)viewDidLoad
{
[super viewDidLoad];
// checking if the file was created and show a message if its created or not.
if ([self dataFilePath]) {
NSLog(#"File Created !");
} else {
NSLog(#"File Not Created !");
}
NSLog(#"File location : %#",documentDirectory);
choice = YES;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL) dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentDirectory = [path objectAtIndex:0];
documentDirectory = [documentDirectory stringByAppendingPathComponent:#"MilmersData.dat"];
return TRUE;
}
- (IBAction)writePlist
{
p.name = textName.text;
p.age = [textAge.text intValue];
[anArray addObject:p];
for (int i=0; i<[anArray count]+1; i++) {
Person *pp = [[Person alloc]init];
pp=[anArray objectAtIndex:i];
NSLog(#"Name: %#",pp.name); // checking the names in pp object but getting null
}
data = [NSKeyedArchiver archivedDataWithRootObject:anArray];
[data writeToFile:documentDirectory options:NSDataWritingAtomic error:nil];
NSLog(#"Array length: %d",[anArray count]); //Always got array count zero.
}
-(IBAction)readPlist
{
NSString *filePath = documentDirectory;
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(#"The array is: %#",array); // it shows that there is nothing in the array
}
#end
I wrote the class for writing .plist files originally but I knew later that I cant store objects in .plist file so I tried so that with archive, thats why the method name have plist in it.
Thank you in advance
Looks like you aren't ever creating an instance of p to add to the array. Try:
Person *p = [[Person alloc] init];
p.name = textName.text;
p.age = [textAge.text intValue];
[anArray addObject:p];
your index limit was also wrong in this loop
for (int i=0; i<[anArray count]; i++) {
NSLog(#"Name: %#", [[anArray objectAtIndex:i] name]);
}
you should really have been seeing a couple of different crashes...
Try adding this in viewDidLoad
[[NSFileManager defaultManager] createFileAtPath:documentDirectory contents:nil error:nil];
It looks like you never do this, and using archives to write to files only works if the file already exists (make sure you only do this once, otherwise every time that view is loaded the file will be emptied of all the data in it). And when you do this
if ([self dataFilePath])
It's pointless, because no matter what it always returns yes, whether the file exists or not.
Does your Person class implement NSCoding?
Specifically you need to implement something like the following in Person.m:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) {
return nil;
}
self.name = [decoder decodeObjectForKey:#"name"];
self.age = [decoder decodeObjectForKey:#"age"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.age forKey:#"age"];
}
I've made a container class to store a single tweet. Its initialized by passing in a dictionary object which is a single tweet.
I then store an array of these 'tweets' which I process through to display in a table.
The project is now finished and I am reviewing everything at the moment and I was wondering is there a better way to do this in the future. Is the memory handled correctly. I declare the string member vars with 'copy' and later in the dealloc I use a 'release' rather than just setting them to 'nil'.
Is my init ok or could that be improved?
Tweet.h
#import
#interface Tweet : NSObject
{
NSString * _userName;
NSString * _tweetText;
NSString * _tweetURL;
}
#property (nonatomic, copy) NSString * userName;
#property (nonatomic, copy) NSString * tweetText;
#property (nonatomic, copy) NSString * tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary;
#end
Tweet.m
#implementation Tweet
#synthesize userName = _userName;
#synthesize tweetText = _tweetText;
#synthesize tweetURL = _tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary
{
NSDictionary *aDict = [productsDictionary objectForKey:#"user"];
self.userName = [aDict objectForKey:#"screen_name"];
self.tweetText = [productsDictionary objectForKey:#"text"];
NSRange match;
match = [self.tweetText rangeOfString: #"http://"];
if (match.location != NSNotFound)
{
NSString *substring = [self.tweetText substringFromIndex:match.location];
NSRange match2 = [substring rangeOfString: #" "];
if (match2.location == NSNotFound)
{
self.tweetURL = substring;
}
else
{
self.tweetURL = [substring substringToIndex:match2.location];
}
}
else
{
self.tweetURL = nil;
}
return self;
}
-(void) dealloc
{
[self.tweetText release];
[self.tweetURL release];
[self.userName release];
[super dealloc];
}
#end
Many Thanks,
Code
At first sight, I see no inherent flaws here. That looks fine. I would prefer to do:
-(void) dealloc
{
[_tweetText release];
[_tweetURL release];
[_userName release];
[super dealloc];
}
But what you do is good as well.
So in my model I have the following code... I am successfully able to return each individual value. I want to know how am I able to return the entire speakerTable []... Maybe some advice. Thanks!
typedef struct {
NSUInteger speakerID;
NSString * speakerName;
NSString * speakerPosition;
NSString * speakerCompany;
} SpeakerEntry;
static const SpeakerEntry speakerTable [] =
{
{0, #"name", #"position", #"company"},
{1, #"name", #"position", #"company"},
{-1, nil, nil, nil}
};
This works correctly...
-(NSString *) stringSpeakerCompanyForId:(NSUInteger) identifier{
NSString * returnString = nil;
if ([self helpCount] > identifier) {
returnString = speakerTable[identifier].speakerCompany;
}
return returnString;
}
This does not work at all..
-(id) getSpeaker{
//if ([speakerTable[0].speakerName isKindOfClass:[NSString class]])
// NSLog(#"YES");
NSArray * myArray3 = [NSArray arrayWithArray:speakerTable];
return myArray3;
}
arrayWithArray expects an NSArray, not a C array.
The first one works because you are using it like a C array.
Alternatively - don't use a struct, use an object instead:
Create a class called Speaker.
In Speaker.h
#interface Speaker : NSObject {}
#property (nonatomic, assign) NSUinteger id;
#property (nonatomic, copy) NSString name;
#property (nonatomic, copy) NSString position;
#property (nonatomic, copy) NSString company;
- (void)initWithId:(NSUInteger)anId name:(NSString *)aName position:(NSString *)aPosition company:(NSString *)aCompany;
#end
in Speaker.m
#import "Speaker.h"
#implementation Speaker
#synthesize id, name, position, company;
- (void)initWithId:(NSUInteger)anId name:(NSString *)aName position:(NSString *)aPosition company:(NSString *)aCompany {
if (!([super init])) {
return nil;
}
id = anId;
NSString name = [[NSString alloc] initWithString:aName];
NSString position = [[NSString alloc] initWithString:aPosition];
NSString company = [[NSString alloc] initWithString:aCompany];
return self;
}
- (void)dealloc {
[name release];
[position release];
[company release];
[super dealloc];
}
#end
And now in your calling code you can create an immutable array of speakers with:
Speaker *speaker0 = [[Speaker alloc] initWithId:0 name:#"name0" position:#"position0" company:#"company0"];
Speaker *speaker1 = [[Speaker alloc] initWithId:1 name:#"name1" position:#"position1" company:#"company1"];
Speaker *speakerNull = [[Speaker alloc] initWithId:-1 name:nil position:nil company:nil];
NSArray *speakerArray [[NSArray arrayWithObjects: speaker0, speaker1, speakerNull] retain]
[speaker0 release];
[speaker1 release];
[speakerNull release];
note: this is typed straight in, so feel free to mention/correct typos or errors
The method arrayWithArray takes in an NSArray as an argument, not a C array.
I have a method to generate some strings based on the values of some variables I get from I picker controller that is presented modally. I get the values back in the delegate method fine and I can assign them to some properties in my view controller successfully. When I call my method to do the updating, I send the values to the console via NSLog and they show up fine there too. But if I put a breakpoint at the end of my method and look at their values just below the NSLogs, the hover-message says out of scope.
EDIT: I went ahead and added the rest of the head and implementation. Maybe someone can see
#import <UIKit/UIKit.h>
#import "SourcePickerViewController.h"
#import "Chemical.h"
#interface AdjustViewController : UIViewController <SourcePickerViewControllerDelegate>{
// IB controls
UITextField *sourceField;
UITextField *volumeField;
UILabel *startingLabel;
UILabel *targetLabel;
UITextView *adviceLabel;
// Setup variables for the kind of chemical
int numberOfComponents;
NSDictionary *dictionaryOfSources;
// Local ivars
float percentRemove;
float gallonsRemove;
float selectedChemSourceAmount;
int delta;
NSString *selectedChemName;
NSString *selectedChemConcentration;
float selectedChemConstant;
BOOL selectedChemIsLiquid;
NSString *compositeName;
NSString *messageBody;
NSString *adviceMessage;
}
#property (nonatomic, retain) IBOutlet UITextField *sourceField;
#property (nonatomic, retain) IBOutlet UITextField *volumeField;
#property (nonatomic, retain) IBOutlet UILabel *startingLabel;
#property (nonatomic, retain) IBOutlet UILabel *targetLabel;
#property (nonatomic, retain) IBOutlet UITextView *adviceLabel;
#property (nonatomic, retain) NSString *selectedChemName;
#property (nonatomic, retain) NSString *selectedChemConcentration;
#property float selectedChemConstant;
#property BOOL selectedChemIsLiquid;
#property (nonatomic, retain) NSString *compositeName;
#property (nonatomic, retain) NSString *messageBody;
#property (nonatomic, retain) NSString *adviceMessage;
#property int numberOfComponents;
#property (nonatomic, retain) NSDictionary *dictionaryOfSources;
- (IBAction)backgroundTap:(id)sender;
//- (IBAction)textFieldDoneEditing:(id)sender;
- (IBAction)startingSliderChanged:(id)sender;
- (IBAction)startingSliderFinishedChanging;
- (IBAction)targetSliderChanged:(id)sender;
- (IBAction)targetSliderFinishedChanging;
- (IBAction)getChemicalSource;
- (void)updateAdvice;
#end
#import "AdjustViewController.h"
#implementation AdjustViewController
#synthesize sourceField;
#synthesize volumeField;
#synthesize startingLabel;
#synthesize targetLabel;
#synthesize adviceLabel;
#synthesize numberOfComponents;
#synthesize dictionaryOfSources;
#synthesize compositeName;
#synthesize messageBody;
#synthesize adviceMessage;
#synthesize selectedChemName;
#synthesize selectedChemConcentration;
#synthesize selectedChemConstant;
#synthesize selectedChemIsLiquid;
- (IBAction)backgroundTap:(id)sender {
[sourceField resignFirstResponder];
[volumeField resignFirstResponder];
[self updateAdvice];
}
- (IBAction)startingSliderChanged:(id)sender {
UISlider *slider = (UISlider *)sender;
int progressAsInt = (int)(slider.value + 0.5f);
NSString *newValue = [[NSString alloc] initWithFormat:#"%d", progressAsInt];
startingLabel.text = newValue;
[newValue release];
}
- (IBAction)targetSliderChanged:(id)sender {
UISlider *slider = (UISlider *)sender;
int progressAsInt = (int)(slider.value + 0.5f);
NSString *newValue = [[NSString alloc] initWithFormat:#"%d", progressAsInt];
targetLabel.text = newValue;
[newValue release];
}
- (IBAction)startingSliderFinishedChanging {
// [self updateAdvice];
}
- (IBAction)targetSliderFinishedChanging {
// [self updateAdvice];
}
// Present the picker for chlorine selection
- (IBAction)getChemicalSource {
SourcePickerViewController *sourcePickerViewController = [[SourcePickerViewController alloc] init];
sourcePickerViewController.delegate = self;
NSLog(#"getChemicalSource setting numberOfComponents %d", self.numberOfComponents);
sourcePickerViewController.numberOfComponents = self.numberOfComponents;
NSLog(#"getChemicalSource sending numberOfComponents %d", sourcePickerViewController.numberOfComponents);
sourcePickerViewController.dictionaryOfSources = self.dictionaryOfSources;
[self presentModalViewController:sourcePickerViewController animated:YES];
[sourcePickerViewController release];
}
- (void)updateAdvice {
NSLog(#"--updateAdvice");
NSLog(#" selectedChemical name = %#", selectedChemName);
NSLog(#" selectedChemical concentration = %#", selectedChemConcentration);
NSLog(#" selectedChemical constant = %1.6f", selectedChemConstant);
NSLog(#" selectedChemical is liquid = %d", selectedChemIsLiquid);
// First check to see if there is a source AND volume, otherwise prompt user to enter them
if ([volumeField.text isEqualToString:#""] || [sourceField.text isEqualToString:#""]) {
adviceMessage = #"Enter a source and volume.";
}
// If there IS a source and volume, calculate!
else {
if ([selectedChemConcentration isEqualToString:#""]) { // If there's no concentration, make a string with just the name
compositeName = selectedChemName;
NSLog(#" compositeName without concentration = %#", compositeName);
}
else { // But if there is a concentration, make a string with the name and concentration and a space between.
compositeName = [[NSString alloc] initWithFormat:#"%# %#", selectedChemName, selectedChemConcentration];
NSLog(#" compositeName with concentration = %# %#", compositeName, selectedChemConcentration);
}
delta = [targetLabel.text intValue] - [startingLabel.text intValue]; // The difference between target and starting levels
NSLog(#" delta = %d", delta);
sourceAmount = delta * [volumeField.text intValue] * sourceConstant; // Calculates the amount of source chemical necessary in ounces
NSLog(#" sourceAmount = %1.1f", sourceAmount);
// If delta is positive, add chemical
if (delta > 0) {
NSLog(#">> Delta > 0");
if (selectedChemIsLiquid) {
if (sourceAmount > 128) { // Amount is more than a gallon
sourceAmount = sourceAmount / 128;
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f gal of ", self.title, delta, sourceAmount];
}
else { // Less than a gallon
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f fl oz of ", self.title, delta, sourceAmount];
}
}
else { // Chemical is a solid
if (sourceAmount > 16) { // Amount is more than a pound
sourceAmount = sourceAmount / 16;
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f lb of ", self.title, delta, sourceAmount];
}
else { // Less than a pound
messageBody = [[NSString alloc] initWithFormat:#"To increase %# by %d ppm, add %1.1f oz of ", self.title, delta, sourceAmount];
}
}
adviceMessage = [[NSString alloc] initWithFormat:#"%#%#.", messageBody, compositeName];
}
// If delta is zero, stay the course
if (delta == 0) {
NSLog(#"== Delta = 0");
adviceMessage = #"You're on target. No action necessary.";
}
// If delta is negative, remove water
if (delta < 0) {
NSLog(#"<< Delta < 0");
adviceMessage = #"You're over target. Remove some water.";
}
}
adviceLabel.text = adviceMessage; // Set the advice label
[messageBody release]; // And get rid of message
[compositeName release];
[adviceMessage release];
}
- (void)viewDidLoad {
NSLog(#"AdjustViewController launched");
sourceField.text = #"";
adviceLabel.text = #"";
percentRemove = 0;
gallonsRemove = 0;
delta = 0;
selectedChemSourceAmount = 0;
// [self updateAdvice];
[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.
}
- (void)viewDidUnload {
sourceField = nil;
volumeField = nil;
startingLabel = nil;
targetLabel = nil;
adviceLabel = nil;
dictionaryOfSources = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[sourceField release];
[volumeField release];
[startingLabel release];
[targetLabel release];
[adviceLabel release];
[dictionaryOfSources release];
[super dealloc];
}
#pragma mark -
#pragma mark Picker View Delegate Methods
// Returns the values from the picker if a source was chosen
- (void)sourcePickerViewController:(SourcePickerViewController *)controller
didSelectSource:(NSString *)source
andConcentration:(NSString *)concentration
andConstant:(float)constant
andIsLiquid:(BOOL)isLiquid {
selectedChemName = source;
selectedChemConcentration = concentration;
selectedChemConstant = constant;
selectedChemIsLiquid = isLiquid;
// Update the source textfield. If concentration is empty, just use the source otherwise concatenate them
if ([selectedChemConcentration isEqualToString:#""]) {
sourceField.text = [[NSString alloc] initWithFormat:#"%#", selectedChemName];
}
else {
sourceField.text = [[NSString alloc] initWithFormat:#"%# %#", selectedChemName, selectedChemConcentration];
}
// [self updateAdvice];
NSLog(#"Returned source = %#, concentration = %#, constant = %1.7f, isLiquid = %d", source, concentration, constant, isLiquid);
NSLog(#"selectedChemical.chemName = %#, chemConcentration = %#, chemConstant = %1.7f, chemIsLiquid = %d", selectedChemName, selectedChemConcentration, selectedChemConstant, selectedChemIsLiquid);
[self dismissModalViewControllerAnimated:YES];
}
// Returns from the picker without choosing a new source
- (void)sourcePickerViewController:(SourcePickerViewController *)controller
didSelectCancel:(BOOL)didCancel {
// [self updateAdvice];
NSLog(#"Returned without selecting source");
[self dismissModalViewControllerAnimated:YES];
}
#end
Check this: Strange Descriptions for Arrays in XCode debugger
If this is the case, other objects
like NSStrings will be unavailable as
well
I am a newbie to the iphone app world. So I thought I try my luck with a calculator app.
Unfortunately I am running into an issue where if I press a third key in the calculator the app crashes. Sometimes I get this error EXC_BAD_ACCESS. Here is a code in my CalculatorViewController.m file.
#import "CalculatorViewController.h"
#implementation CalculatorViewController
#synthesize screenText;
- (IBAction)buttonPressed:(id)sender {
NSString *title = [sender titleForState:UIControlStateNormal];
[self collect:title];
}
- (void)collect:(NSString *)digitz {
NSString * newText = nil;
if ([digitz isEqualToString:#"+"]) {
[self add:result];
big_digit = nil;
}
else if ([digitz isEqualToString:#"+"]) {
[self sub:result];
}
else if ([digitz isEqualToString:#"x"]) {
[self multiply:result];
}
else if ([digitz isEqualToString:#"="]) {
[self equate:result];
}
else {
if (big_digit != nil && [big_digit isEqualToString:#"0"] == FALSE)
big_digit = [big_digit stringByAppendingFormat:#"%#",digitz];
else
big_digit = (NSMutableString *) digitz;
result = (int) big_digit;
newText = [[NSString alloc] initWithFormat:
#"%#",big_digit];
}
screenText.text = newText;
[newText release];
}
- (void)add:(int)res {
NSString * newText = nil;
ans = ans + res;
newText = [[NSString alloc] initWithFormat:
#"%#",ans];
screenText.text = newText;
[newText release];
}
Can anyone spot an obvious issue here. Here is the respective header file too.
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController {
UILabel *screenText;
int number;
int result;
int ans;
//NSString *big_digit;
NSMutableString * big_digit ;
}
#property (nonatomic, retain) IBOutlet UILabel *screenText;
- (IBAction)buttonPressed:(id)sender;
- (void)collect:(NSString *)digitz;
- (void)add:(int)num;
- (void)sub:(int)num;
- (void)multiply:(int)num;
- (void)equate:(int)num;
#end
Well, you probably don't want to just cast a string to an integer (ala (int)big_digit). Instead you want to use [big_digit integerValue];
I think what is happening is that your big_digit property is not retained. In this line, you just assign a string to it that is autoreleased:
big_digit = [big_digit stringByAppendingFormat:#"%#",digitz];
On the next pass through, big_digit is != nil, but [big_digit isEqualToString:#"0"] == FALSE fails because big_digit now points to an invalid memory location.
What you want to do is make big_digit a property in your interface, like so...
#property (nonatomic, retain) NSMutableString *big_digit;
I know reading docs sucks, but looking at your code I think you would really find reading through this useful. Memory management in objective c is quite a bit different from regular old C.
http://developer.apple.com/iphone/library/documentation/cocoa/conceptual/memorymgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW1
In your format strings, you're doing this:
newText = [[NSString alloc] initWithFormat:#"%#", ans];
But according to your #interface, ans is an integer. So that line should read:
newText = [[NSString alloc] initWithFormat:#"%d", ans];
since %d is the format specifier for an integer.