Objective-C中的常量

我正在开发一个可可应用程序,我使用常量NSString作为存储我的首选项的键名的方法。

我明白这是一个好主意,因为它允许在必要时轻松更改键。
另外,它是整个“将数据与逻辑分开”的概念。

无论如何,有没有一种好的方法可以让这些常量为整个应用程序定义一次?

我确信有一种简单而智能的方法,但现在我的类只是重新定义了它们使用的方法。

442507 次浏览

如果您想要全局常量之类的东西;一种快速而肮脏的方法是将常量声明放入pch文件中。

最简单的方法:

// Prefs.h#define PREFS_MY_CONSTANT @"prefs_my_constant"

更好的方法:

// Prefs.hextern NSString * const PREFS_MY_CONSTANT;
// Prefs.mNSString * const PREFS_MY_CONSTANT = @"prefs_my_constant";

第二种方法的一个好处是,更改常量的值不会导致整个程序的重建。

正如Abizer所说,您可以将其放入PCH文件中。另一种不那么脏的方法是为所有密钥创建一个包含文件,然后将其包含在您正在使用密钥的文件中,或者将其包含在PCH中。有了它们自己的包含文件,这至少给了您一个查找和定义所有这些常量的地方。

您应该创建一个头文件,如下所示:

// Constants.hFOUNDATION_EXPORT NSString *const MyFirstConstant;FOUNDATION_EXPORT NSString *const MySecondConstant;//etc.

(如果您的代码不会在混合C/C++环境或其他平台上使用,您可以使用extern而不是FOUNDATION_EXPORT。)

您可以将此文件包含在使用常量的每个文件中或项目的预编译标头中。

您在.m文件中定义这些常量,例如:

// Constants.mNSString *const MyFirstConstant = @"FirstConstant";NSString *const MySecondConstant = @"SecondConstant";

Constants.m应该添加到您的应用程序/框架的目标中,以便将其链接到最终产品。

使用字符串常量而不是#defined常量的优点是,您可以使用指针比较(stringInstance == MyFirstConstant)来测试相等性,这比字符串比较([stringInstance isEqualToString:MyFirstConstant])快得多(并且更容易阅读,IMO)。

还有一点要提。如果您需要一个非全局常量,您应该使用static关键字。

示例

// In your *.m filestatic NSString * const kNSStringConst = @"const value";

由于static关键字,此const在文件外部不可见。


@QuinnTaylor的轻微更正静态变量在编译单元中可见。通常,这是一个单一的. m文件(如本例所示),但如果您在其他地方包含的标头中声明它,它可能会咬您,因为您会在编译后收到链接器错误

尝试使用class方法:

+(NSString*)theMainTitle{return @"Hello World";}

我有时用它。

接受的(正确的)答案是“您可以将此[Constants. h]文件…包含在项目的预编译标头中”。

作为一个新手,如果没有进一步的解释,我很难做到这一点——方法如下:在您的YourAppNameHere-Prefix.pch文件中(这是Xcode中预编译标头的默认名称),导入您的Constants. h#0块内

#ifdef __OBJC__#import <UIKit/UIKit.h>#import <Foundation/Foundation.h>#import "Constants.h"#endif

另请注意,Constants. h和Constants. m文件中除了接受的答案中描述的内容外,不应包含任何其他内容。(没有接口或实现)。

// Prefs.hextern NSString * const RAHUL;
// Prefs.mNSString * const RAHUL = @"rahul";

我通常使用Barry Wark和Rahul Gupta发布的方式。

虽然,我不喜欢在. h和. m文件中重复相同的单词。请注意,在以下示例中,两个文件中的行几乎相同:

// file.hextern NSString* const MyConst;
//file.mNSString* const MyConst = @"Lorem ipsum";

因此,我喜欢做的是使用一些C预处理器机器。让我来解释一下这个例子。

我有一个定义宏STR_CONST(name, value)的头文件:

// StringConsts.h#ifdef SYNTHESIZE_CONSTS# define STR_CONST(name, value) NSString* const name = @ value#else# define STR_CONST(name, value) extern NSString* const name#endif

在我的. h/. m对中,我想定义常量,我执行以下操作:

// myfile.h#import <StringConsts.h>
STR_CONST(MyConst, "Lorem Ipsum");STR_CONST(MyOtherConst, "Hello world");
// myfile.m#define SYNTHESIZE_CONSTS#import "myfile.h"

瞧,我只有. h文件中有关常量的所有信息。

对@Krizz的建议进行了轻微修改,以便在常量头文件包含在PCH中时正常工作,这是相当正常的。由于原始文件已导入PCH,它不会将其重新加载到.m文件中,因此您不会得到任何符号,链接器也不满意。

但是,以下修改允许它工作。它有点复杂,但它有效。

您需要3文件,.h文件具有常量定义,.h文件和.m文件,我将分别使用ConstantList.hConstants.hConstants.mConstants.h的内容很简单:

// Constants.h#define STR_CONST(name, value) extern NSString* const name#include "ConstantList.h"

Constants.m文件如下所示:

// Constants.m#ifdef STR_CONST#undef STR_CONST#endif#define STR_CONST(name, value) NSString* const name = @ value#include "ConstantList.h"

最后,ConstantList.h文件中包含实际声明,仅此而已:

// ConstantList.hSTR_CONST(kMyConstant, "Value");…

有几件事需要注意:

  1. 我必须在.m文件中重新定义宏,然后将其#undef化以供使用。

  2. 我还必须使用#include而不是#import才能正常工作并避免编译器看到以前预编译的值。

  3. 这将需要在任何值更改时重新编译您的PCH(可能还有整个项目),如果它们正常分离(和复制),则情况并非如此。

希望这对某人有帮助。

我使用单例类,这样我就可以模拟类并在测试需要时更改常量。常量类如下所示:

#import <Foundation/Foundation.h>
@interface iCode_Framework : NSObject
@property (readonly, nonatomic) unsigned int iBufCapacity;@property (readonly, nonatomic) unsigned int iPort;@property (readonly, nonatomic) NSString * urlStr;
@end
#import "iCode_Framework.h"
static iCode_Framework * instance;
@implementation iCode_Framework
@dynamic iBufCapacity;@dynamic iPort;@dynamic urlStr;
- (unsigned int)iBufCapacity{return 1024u;};
- (unsigned int)iPort{return 1978u;};
- (NSString *)urlStr{return @"localhost";};
+ (void)initialize{if (!instance) {instance = [[super allocWithZone:NULL] init];}}
+ (id)allocWithZone:(NSZone * const)notUsed{return instance;}
@end

它是这样使用的(注意使用常量c的速记-它每次都保存键入[[Constants alloc] init]):

#import "iCode_FrameworkTests.h"#import "iCode_Framework.h"
static iCode_Framework * c; // Shorthand
@implementation iCode_FrameworkTests
+ (void)initialize{c  = [[iCode_Framework alloc] init]; // Used like normal class; easy to mock!}
- (void)testSingleton{STAssertNotNil(c, nil);STAssertEqualObjects(c, [iCode_Framework alloc], nil);STAssertEquals(c.iBufCapacity, 1024u, nil);}
@end

我自己有一个专门用于声明常量NSStrings用于首选项的标头,如下所示:

extern NSString * const PPRememberMusicList;extern NSString * const PPLoadMusicAtListLoad;extern NSString * const PPAfterPlayingMusic;extern NSString * const PPGotoStartupAfterPlaying;

然后在随附的. m文件中声明它们:

NSString * const PPRememberMusicList = @"Remember Music List";NSString * const PPLoadMusicAtListLoad = @"Load music when loading list";NSString * const PPAfterPlayingMusic = @"After playing music";NSString * const PPGotoStartupAfterPlaying = @"Go to startup pos. after playing";

这种方法对我很有用。

编辑:请注意,如果字符串在多个文件中使用,这效果最好。如果只有一个文件使用它,您可以在使用字符串的. m文件中执行#define kNSStringConstant @"Constant NSString"

如果你喜欢命名空间常量,你可以利用struct,周五问答2011-08-19:命名空间常量和函数

// in the headerextern const struct MANotifyingArrayNotificationsStruct{NSString *didAddObject;NSString *didChangeObject;NSString *didRemoveObject;} MANotifyingArrayNotifications;
// in the implementationconst struct MANotifyingArrayNotificationsStruct MANotifyingArrayNotifications = {.didAddObject = @"didAddObject",.didChangeObject = @"didChangeObject",.didRemoveObject = @"didRemoveObject"};

如果你想从目标c中调用这样的NSString.newLine;,并且你希望它是静态常量,你可以在swft中创建这样的东西:

public extension NSString {@objc public static let newLine = "\n"}

你有很好的可读性常量定义,并且可以从你选择的类型中获得,而stile有界于类型的上下文。