开发者

Generate @property implementations with C preprocessor (uppercase a character in the preprocessor)

开发者 https://www.devze.com 2023-03-22 01:38 出处:网络
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 prepro

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消