Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you remove one-time-use class variables from Objective-C code?

I'm writing some Objective-C code and I've frequently hit the situation where I have to use a class variable to store a value for one time use. After consuming it I no longer need it. To me, storing this value in a class variable seems like code smell. Really the value should be passed in as a parameter to the methods I'm using.

I run across this usually when I'm consuming delegates. As an example, I have a UI with multiple buttons used to load and display a UIActionSheet when they're tapped. This action sheet contains a date picker which sets a value for a UILabel when the action sheet is dismissed.

- (IBAction)setPurchaseDateTapped {
    self.activeField = purchaseDate;
    [self loadDatePickerActionSheet:@"Edit Purchase Date"];
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    self.activeField.text = value_from_UIActionSheet;
}

As you can see here, the actionSheet clickedButtonAtIndex callback doesn't allow me to pass the activeField so I have to use a class variable. It seems more correct to write this:

- (void)actionSheet:(UIActionSheet *)actionSheet parameterValue:(id)parameter {
    parameter.text = value_from_UIActionSheet;
}

I believe(?) that I can subclass the UIActionSheet and UIActionSheet delegate and add the signatures that I require, but again this seems like more effort than it's worth.

So my question is what is the best way to do what I'm trying to do?

I don't necessarily want to change the date picker/action sheet interface that I've created (although if there's a better pattern for setting multiple dates on a UIView while keeping the DatePicker out of the way, I'm all ears.)

like image 868
Gavin Miller Avatar asked Jan 25 '10 16:01

Gavin Miller


3 Answers

In this case, I think a simple subclass of UIActionSheet would be the way to go:

@interface SpecialActionSheet : UIActionSheet
{
    id parameter;
}
@property (assign) id parameter;
@end

@implementation SpecialActionSheet
@synthesize parameter;
@end

That should be enough, since all you want to do is have the actionSheet keep a parameter. Now, your code might look as follows:

- (void)loadDatePickerActionSheet:(NSString *)caption forField:(UITextField *)field {
    //...
    datePickerActionSheet.parameter = field;
}

- (IBAction)setPurchaseDateTapped {
    [self loadDatePickerActionSheet:@"Edit Purchase Date" forField:purchaseDate];
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    UITextField * field = ((SpecialActionSheet *)actionSheet).parameter;
    field.text = value_from_UIActionSheet;
}
like image 169
e.James Avatar answered Oct 22 '22 10:10

e.James


My usual approach in these situations is to use the tag property on UIAlertViews, and do a switch on it (it's an integer). It's not quite as nice as having a string or something to pass down, but if you've got multiple alerts, it's an easy way to disambiguate. For example:

...
actionSheet.tag = [fields indexOfObject: self.activeField];
...        //fields is an NSArray of all the field objects I might have on the screen


- (void)actionSheet:(UIActionSheet *)actionSheet parameterValue:(id)parameter {
    [[field objectAtIndex: actionSheet.tag] setText: value_from_UIActionSheet];
}
like image 22
Ben Gottlieb Avatar answered Oct 22 '22 10:10

Ben Gottlieb


Another solution is to use associative storage.

UIActionSheet might already have it. You can test it by

[myActionSheet setValue:@"test value" forKey:@"testKey];
NSLog(@"%@",[myActionSheet valueForKey:@"testKey];

Associative storage is rather nifty if not overused.

like image 30
TechZen Avatar answered Oct 22 '22 12:10

TechZen