Generate @property implementations with C preprocessor (uppercase a character in the preprocessor)
I may be trying to abuse the preprocessor. I want to see if what I have in mind is even possible.
My class has @properties that all have the same bodies. I want to generate these bodies with a preprocessor macro. E.g.:
- (float) accelerometerSensitivity {
return [dict floatForSelector:_cmd or:1];
}
- (void) setAccelerometerSensitivity:(float) n {
[dict setFloat:n forSelector:_cmd];
[dict writeToFile:[self globalDataFilename] atomically:YES];
}
- (float) returnSpringTension {
return [dict floatForSelector:_cmd or:0];
}
- (void) setReturnSpringTension:(float) n {
[dict setFloat:n forSelector:_cmd];
[dict writeToFile:[self globalDataFilename] atomically:YES];
}
// set*ForSelector methods are in a category on NSMutableDictionary and depend on a function that translates selectors into strings:
// NSString* keyFromSelector(SEL selector);
The idea is that instead of using string literals (or string constants) as keys into the dictionary, I derive the string from the selector name. This way I am sure that the spelling of the key matches the property name and essentially get the benefit of compile-time validation of dictionary keys.
What I want to do is say something like SELECTOR_PROPERY(accelerometerSensitivity)
and have it expand into the the getter and the setter. The main difficulty I have in implementing this as a pre-processor macro is generating the setter name from the property name. I need to uppercase the first letter of the property name, and I have no idea how to do that in the开发者_StackOverflow社区 preprocessor.
Nope, you can't do that.
But, you can combine identifiers, so in theory you could define this as:
MACRO(A,a,ccelerometerSensitivity)
It's somewhat klugey, but it's more terse than the alternative.
Here's how I'd do it:
#define MACRO(_a) { \
const char *name = #_a; \
NSString *getterName = [NSString stringWithUTF8String:name]; \
NSString *setterName = [NSString stringWithFormat:@"set%c%s:", toupper(name[0]), (name+1)]; \
NSLog(@"getter name: %@", getterName); \
NSLog(@"setter name: %@", setterName); \
}
Basically, you stringify the macro parameter, then use a simple C function to uppercase the first letter, and use an offset to get everything after the first letter.
Now when you do this:
MACRO(foo);
MACRO(bar);
It logs this:
2011-07-19 21:21:24.798 EmptyFoundation[16016:903] getter name: foo
2011-07-19 21:21:24.800 EmptyFoundation[16016:903] setter name: setFoo:
2011-07-19 21:21:24.801 EmptyFoundation[16016:903] getter name: bar
2011-07-19 21:21:24.802 EmptyFoundation[16016:903] setter name: setBar:
HOWEVER, these are strings. You can't use them as method names. Sorry. :(
Actually, you probably really don't want to do this purely for architectural reasons.
You'll likely be better off if you:
separate the notion of setting state from persisting state. That you are causing I/O with every single tiny little change is horribly inefficient. It is also a mode rife with potential for problems; what happens if you move to a UI where the values track the UI continuously? ... you really don't want disk I/O for every time a dial/slider is tracked under a finger!
use @synthesize for all your @properties and don't even declare ivars. Leverage the tool's ability to generate exactly correct setters / getters for you.
that code looks an awful lot like you've re-invented NSUserDefaults? Use NSUserDefaults for any user preference kinda stuff.
精彩评论