【问题标题】:Generate @property implementations with C preprocessor (uppercase a character in the preprocessor)使用 C 预处理器生成 @property 实现(预处理器中的大写字符)
【发布时间】:2011-07-20 03:59:05
【问题描述】:

我可能试图滥用预处理器。我想看看我的想法是否可行。

我的班级有@properties,它们都具有相同的主体。我想用预处理器宏生成这些主体。例如:

- (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);

我的想法是,我不使用字符串文字(或字符串常量)作为字典的键,而是从选择器名称中派生字符串。这样我就可以确定键的拼写与属性名称匹配,并且从本质上获得了字典键的编译时验证的好处。

我想做的是说SELECTOR_PROPERY(accelerometerSensitivity) 之类的内容,然后将其扩展为getter 和setter。我将其实现为预处理器宏的主要困难是从属性名称生成设置器名称。我需要将属性名称的第一个字母大写,但我不知道如何在预处理器中这样做。

【问题讨论】:

    标签: objective-c c-preprocessor


    【解决方案1】:

    Nope, you can't do that.

    但是,您可以组合标识符,因此理论上您可以将其定义为:

    MACRO(A,a,ccelerometerSensitivity)
    

    它有点笨拙,但比替代方案更简洁。

    【讨论】:

    • 是的,这就是我正在考虑的后备方案。感谢您确认 CPP 没有表达能力来进行我想要的那种操作。
    【解决方案2】:

    我会这样做:

    #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); \
    }
    

    基本上,您将宏参数字符串化,然后使用简单的 C 函数将第一个字母大写,并使用偏移量获取第一个字母之后的所有内容。

    现在当你这样做时:

    MACRO(foo);
    MACRO(bar);
    

    它记录了这个:

    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:
    

    但是,这些是 字符串。您不能将它们用作方法名称。对不起。 :(

    【讨论】:

      【解决方案3】:

      实际上,您可能真的不想纯粹出于架构原因这样做。

      如果你满足以下条件,你可能会过得更好:

      • 设置状态的概念与持久状态分开。每一个微小的变化都会导致 I/O 非常低效。这也是一种充满潜在问题的模式;如果您移动到值连续跟踪 UI 的 UI,会发生什么? ...您真的不希望每次在手指下跟踪拨号/滑块时都需要磁盘 I/O!

      • 对所有@properties 使用@synthesize,甚至不要声明ivars。利用该工具为您生成完全正确的 setter / getter 的能力。

      • 那个代码看起来很像你重新发明了 NSUserDefaults?将 NSUserDefaults 用于任何用户偏好。

      【讨论】:

      • 我的问题不是问我想做什么。它我想做什么。 --- 我想要生成的代码是已经在源代码中的代码,它的执行令我满意。我只是问如何生成它而不是复制粘贴它。 --- 除了字典,本例中没有 ivars。当我准备好保留用户设置时,这是我上的一门课。我支持你分离状态和持久性的概念——这是持久性部分。 --- NSUserDefaults 不是答案。我的 UI 更加复杂,我希望它位于应用程序内部,而不是设置的角落。
      • NSUserDefaults 完全可以在您的应用中使用。它不需要进入设置。这就是用户默认值的全部意义。并且用户默认值通常会避免在每次状态更改时向磁盘发送垃圾邮件。
      • 感谢您的意见。我很高兴您知道我的应用程序中可用的内容。我对 NSUserDefaults 的经验有限,但在我使用它的地方,它会在设置应用程序中创建 UI 元素(这都是在 iOS 上)。在任何情况下,NSUserDefaults 都使用字符串键来查找值,而我的问题的重点是我想避免使用字符串文字(或解析为文字的常量)作为键。我的经验是,在应用程序中有十几个这样的字符串键时,拼写错误会变得很麻烦。如果我使用 NSUserDefaults,只有宏的主体会改变。
      • @iter "...它在设置应用程序中创建 UI 元素" 是完全错误的陈述。 Settings 应用程序中显示的唯一元素是在 Settings.bundle 中定义的元素。此外,如果您担心拼写错误,请将字符串定义为常量并按名称引用它们。 poof 拼写问题解决了,因为如果你拼错了一个常量名,编译器会为你捕捉到它。
      • 如果你看一下 Apple 是如何做到的,通常会有一个全局常量或 #define,如 kNNLogIdentifier,其值类似于 @"AppKit Logging Identifier";。代码中使用的变量表示其作用和来源,而编译后的值更具描述性。
      猜你喜欢
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 2017-09-18
      • 2012-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多