I have a UIViewController
that presents another UIViewController
with a picker and returns four values. I also have a custom class called Chemical that can hold these values. In the delegate method that receives the values (the didSelectSource
one in AdjustViewController
) I put a breakpoint and I can see that the proper values come back, but when I try to assign them to my local Chemical
called selectedChemical
, the values don't stick and I get an EXC_BAD_ACCESS
. When I hover over the selectedChemical.chemName
it says, "out of scope".
I don't get it.
#import <Foundation/Foundation.h>
@interface Chemical : NSObject {
NSString *chemName;
NSString *chemConcentration;
float chemConstant;
BOOL chemIsLiquid;
}
@property (nonatomic, retain) NSString *chemName;
@property (nonatomic, retain) NSString *chemConcentration;
@property float chemConstant;
@property BOOL chemIsLiquid;
- (id)initWithChemical:(NSString *)chemical
andConcentration:(NSString *)concentration
andConstant:(float)constant
andIsLiquid:(BOOL)isLiquid;
@end
#import "Chemical.h"
@implementation Chemical
@synthesize chemName;
@synthesize chemConcentration;
@synthesize chemConstant;
@synthesize chemIsLiquid;
- (id)initWithChemical:(NSString *)chemical
andConcentration:(NSString *)concentration
andConstant:(float)constant
andIsLiquid:(BOOL)isLiquid {
if((self = [super init])) {
self.chemName = chemical;
self.chemConcentration = concentration;
self.chemConstant = constant;
self.chemIsLiquid = isLiquid;
}
return self;
}
@end
#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)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);
}
delta = [targetLabel.text intValue] - [startingLabel.text intValue]; // The difference between target and starting levels
NSLog(@" delta = %d", delta);
selectedChemSourceAmount = delta * [volumeField.text intValue] * selectedChemConstant; // Calculates the amount of source chemical necessary in ounces
NSLog(@" selectedChemSourceAmount = %1.1f", selectedChemSourceAmount);
// If delta is positive, add chemical
if (delta > 0) {
NSLog(@">> Delta > 0");
if (selectedChemIsLiquid) {
if (selectedChemSourceAmount > 128) { // Amount is more than a gallon
selectedChemSourceAmount = selectedChemSourceAmount / 128;
messageBody = [[NSString alloc] initWithFormat:@"To increase %@ by %d ppm, add %1.1f gal of ", self.title, delta, selectedChemSourceAmount];
}
else { // Less than a gallon
messageBody = [[NSString alloc] initWithFormat:@"To increase %@ by %d ppm, add %1.1f fl oz of ", self.title, delta, selectedChemSourceAmount];
}
}
else { // Chemical is a solid
if (selectedChemSourceAmount > 16) { // Amount is more than a pound
selectedChemSourceAmount = selectedChemSourceAmount / 16;
messageBody = [[NSString alloc] initWithFormat:@"To increase %@ by %d ppm, add %1.1f lb of ", self.title, delta, selectedChemSourceAmount];
}
else { // Less than a pound
messageBody = [[NSString alloc] initWithFormat:@"To increase %@ by %d ppm, add %1.1f oz of ", self.title, delta, selectedChemSourceAmount];
}
}
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