我正在开发一个Cocoa应用程序,并且我使用常量NSString
s作为存储我的首选项的键名的方法.
我知道这是一个好主意,因为它可以在必要时轻松更改密钥.此外,它是整个'将您的数据与逻辑分离'的概念.
无论如何,是否有一种很好的方法可以为整个应用程序定义一次这些常量?我确信这是一种简单而聪明的方式,但是现在我的课程只是重新定义了他们使用的课程.
你应该创建一个像头文件
// Constants.h FOUNDATION_EXPORT NSString *const MyFirstConstant; FOUNDATION_EXPORT NSString *const MySecondConstant; //etc.
(如果您的代码不会在混合C/C++环境或其他平台上使用extern
,FOUNDATION_EXPORT
则可以使用而不是代码)
您可以将此文件包含在使用常量的每个文件中,也可以包含在项目的预编译头中.
您可以在.m文件中定义这些常量
// Constants.m NSString *const MyFirstConstant = @"FirstConstant"; NSString *const MySecondConstant = @"SecondConstant";
应将Constants.m添加到应用程序/框架的目标中,以便将其链接到最终产品.
使用字符串常量而不是#define
'd常量的优点是,您可以使用指针比较(stringInstance == MyFirstConstant
)来测试相等性,这比字符串比较([stringInstance isEqualToString:MyFirstConstant]
)快得多(并且更容易阅读,IMO).
最简单的方法:
// Prefs.h #define PREFS_MY_CONSTANT @"prefs_my_constant"
更好的方法:
// Prefs.h extern NSString * const PREFS_MY_CONSTANT; // Prefs.m NSString * const PREFS_MY_CONSTANT = @"prefs_my_constant";
第二个好处的一个好处是更改常量的值不会导致重建整个程序.
还有一件事需要提及.如果需要非全局常量,则应使用static
关键字.
例
// In your *.m file static NSString * const kNSStringConst = @"const value";
由于该static
关键字,此const在文件外部不可见.
@QuinnTaylor进行小修正:静态变量在编译单元中可见.通常,这是一个单独的.m文件(如本例所示),但是如果你在其他地方包含的头文件中声明它,它会咬你,因为你在编译后会遇到链接器错误
接受的(和正确的)答案说"你可以在项目的预编译头中包含这个[Constants.h]文件."
作为一个新手,我很难做到这一点而没有进一步的解释 - 这是如何:在你的YourAppNameHere-Prefix.pch文件中(这是Xcode中预编译头的默认名称),在#ifdef __OBJC__
块内导入Constants.h .
#ifdef __OBJC__ #import#import #import "Constants.h" #endif
另请注意,除了接受的答案中描述的内容之外,Constants.h和Constants.m文件中除了其他内容之外绝不应包含任何其他内容.(没有接口或实现).
我通常使用Barry Wark和Rahul Gupta发布的方式.
虽然,我不喜欢在.h和.m文件中重复相同的单词.请注意,在以下示例中,两个文件中的行几乎相同:
// file.h extern NSString* const MyConst; //file.m NSString* 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 #importSTR_CONST(MyConst, "Lorem Ipsum"); STR_CONST(MyOtherConst, "Hello world"); // myfile.m #define SYNTHESIZE_CONSTS #import "myfile.h"
et voila,我只有.h文件中有关常量的所有信息.
我自己有一个标题专门用于声明用于首选项的常量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";
这种方法对我很有帮助.
编辑:请注意,如果在多个文件中使用字符串,这将最有效.如果只有一个文件使用它,则可以#define kNSStringConstant @"Constant NSString"
在使用该字符串的.m文件中执行.
稍微修改一下@Krizz的建议,这样如果将常量头文件包含在PCH中它就能正常工作,这是很正常的.由于原始文件被导入到PCH中,因此它不会将其重新加载到.m
文件中,因此您没有符号,链接器也不满意.
但是,以下修改允许它工作.这有点令人费解,但它确实有效.
你需要3档,.h
其中有常量定义,该文件.h
的文件和.m
文件,我将使用ConstantList.h
,Constants.h
并Constants.m
分别.Constants.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.h STR_CONST(kMyConstant, "Value"); …
有几点需要注意:
我不得不重新定义宏.m
文件后, #undef
荷兰国际集团它的宏观使用.
我还必须使用#include
而不是#import
为了正常工作,并避免编译器看到以前预编译的值.
这将需要在任何值更改时重新编译您的PCH(可能还有整个项目),如果它们正常分开(和重复)则不是这种情况.
希望对某人有帮助.
// Prefs.h extern NSString * const RAHUL; // Prefs.m NSString * const RAHUL = @"rahul";
正如Abizer所说,你可以把它放到PCH文件中.另一种不那么脏的方法是为所有密钥创建一个包含文件,然后将其包含在您正在使用密钥的文件中,或者将其包含在PCH中.将它们放在自己的包含文件中,至少可以为您提供一个查找和定义所有这些常量的位置.
如果你想要像全局常量那样的东西; 一种快速的方法是将常量声明放入pch
文件中.
尝试使用类方法:
+(NSString*)theMainTitle { return @"Hello World"; }
我有时会用它.
如果你喜欢名称空间常量,你可以利用struct,Friday Q&A 2011-08-19:Namespaced Constants and Functions
// in the header extern const struct MANotifyingArrayNotificationsStruct { NSString *didAddObject; NSString *didChangeObject; NSString *didRemoveObject; } MANotifyingArrayNotifications; // in the implementation const struct MANotifyingArrayNotificationsStruct MANotifyingArrayNotifications = { .didAddObject = @"didAddObject", .didChangeObject = @"didChangeObject", .didRemoveObject = @"didRemoveObject" };
我使用单例类,以便我可以模拟类并在必要时更改常量以进行测试.常量类看起来像这样:
#import@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