在Objective-C中创建常量的最佳方法是什么


156

我正在创建一个Reddit客户用于学习目的。我需要一个带有常量的文件。我正在考虑将文件导入文件中Reddit-Prefix.pch以使常量可用于所有文件。这是做事的好方法吗?另外,我已经完成了研究,发现了几种创建常量的方法,但是我不知道该使用哪种方法:

  • #define 巨集
  • const
  • static const
  • extern const
  • enum

那么哪种方式是首选方式?有什么约定?我知道“取决于”,但我的问题更具体地是:这些解决方案中的每一个都有哪些用例?

另外,如果使用extern const,是否需要导入文件,或者常量将在不导入文件的情况下全局可用?

我可以从逻辑上得出的结论是,enum在定义自定义错误域之类的东西时,这是最佳选择(我实际上是对的吗?)。但是其他呢?


stackoverflow.com/questions/11153156 / ...请访问此链接...您的解决方案在此信息中
用户1531343 2013年

3
@BhavikKama:与两个特定的解决方案相比,这是一个狭窄的问题。
Peter Hosey 2013年

对于-静态const,#define,枚举,此链接是有用的stackoverflow.com/questions/1674032/static-const-vs-define-in-c提供了有关const的这3种替代方法的很好的解释
用户1531343 2013年

enum仅对整数值有用。#define常量可以是任何数据类型。
rmaddy13年

conststatic constextern const,除范围外都相同。因此,实际上只有三个选择。
rmaddy13年

Answers:


385

第一个问题是您希望常量具有什么范围,这实际上是两个问题:

  • 这些常量是特定于单个类的吗,还是在整个应用程序中都包含这些常量有意义?
  • 如果它们是特定于类的,它们是供类的客户使用还是仅在类内使用?

如果它们是特定的并且在单个类内部,则将它们声明为static const.m文件的顶部,如下所示:

static NSString *const MyThingNotificationKey = @"MyThingNotificationKey";

如果它们属于单个类,但应该由其他类公共/使用,则extern在标头中声明它们,并在.m中定义它们:

//.h
extern NSString *const MyThingNotificationKey;

//.m
NSString *const MyThingNotificationKey = @"MyThingNotificationKey";

如果它们是全局的,则在标头中声明它们,并在相应的模块中定义它们,特别是对于那些常量。

您可以将它们与不同的常量混合使用,并以不同的级别使它们成为一个整体,对于不属于它们的不同的全局常量,可以将它们放在单独的模块中,如果有的话,每个模块都有自己的标头想。

为什么不#define呢?

过去的答案是“宏没有类型信息”,但是当今的编译器非常聪明地对文字(变量扩展为宏)进行所有类型检查。

现代的答案是因为调试器不会知道您的宏。您不能[myThing addObserver:self forKey:MyThingNotificationKey]在调试器命令中说出是否MyThingNotificationKey是宏。调试器只能知道它是否是变量。

为什么不enum呢?

好吧,rmaddy在注释中击败了我:enum只能定义整数常量。诸如序列号,位掩码,四字节代码等之类的东西。

出于这些目的,enum它很棒,您绝对应该使用它。(更妙的是,使用NS_ENUMNS_OPTIONS)。对于其他的东西,你必须用别的东西; enum除整数外不执行任何操作。

和其他问题

我正在考虑将文件导入Reddit-Prefix.pch文件中,以使常量可用于所有文件。这是做事的好方法吗?

可能无害,但可能过度。将常量标头导入所需的位置。

这些解决方案的用例分别是什么?

  • #define:相当有限。老实说,我不确定是否有足够的理由将其用于常量。
  • const:最适合局部常量。同样,您还必须将其用于在标头中声明并正在定义的内容。
  • static const:最适合于特定于文件(或特定于类)的常量。
  • extern const注意:在标头中导出常量时,必须使用它。

另外,如果使用extern const,是否需要导入文件,或者常量将在不导入文件的情况下全局可用?

您需要在使用文件的每个文件中或在前缀标头中导入文件。


3
为什么不完全static NSString *const.h文件中使用?
Iulian Onofrei 2015年

3
@IulianOnofrei:如果在应用程序而不是框架中,则可以。如果这样做static NSString *const foo = @"foo";,那么标题将确定字符串是什么,并且在任何地方都必须相同—如果您更改了字符串,并且不同的参与者使用具有不同字符串的标题的不同版本,则字符串在运行时将不匹配时间。在框架中,您只想提供对符号的访问,并让框架成为该符号真实值的唯一来源,因此每个人都可以从一个地方获取相同的字符串。那就是extern让你得到的。
彼得·霍西

关于#defines的附加说明:不能保证它们在内存中具有相同的地址(取决于它们的声明方式,每次使用它们都可能分配一个新实例),因此使用myObject == MyDefine将不会总是按预期方式工作,但是myObject == MyStaticConst会的。
Ben Leggiero

是否有意义拼写一样static NSString *const,而不是static NSString const*?有什么区别吗?
kokos8998 '16

@ kokos8998有什么不​​同吗?是的,它确实。static NSString const *与相同,static const NSString *并且表示“指向常量NSString的(可变)指针”-在这里没有用,因为NSString已经是不可变的。您只需要static NSString * const-就是“指向NSString的恒定指针”
David

8

FOUNDATION_EXPORT

考虑使用FOUNDATION_EXPORT兼容性,extern因为它是在基础中定义的,并且可以编译为C,C ++和Win32的兼容格式。

如NSObjCRuntime.h中所定义

#if defined(__cplusplus)
#define FOUNDATION_EXTERN extern "C"
#else
#define FOUNDATION_EXTERN extern
#endif

#if TARGET_OS_WIN32

    #if defined(NSBUILDINGFOUNDATION)
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllexport)
    #else
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllimport)
    #endif

    #define FOUNDATION_IMPORT FOUNDATION_EXTERN __declspec(dllimport)

#else
    #define FOUNDATION_EXPORT  FOUNDATION_EXTERN
    #define FOUNDATION_IMPORT FOUNDATION_EXTERN
#endif
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.