Objective-C中的常量
我正在开发一个Cocoa应用程序,而且我正在使用常量NSString
s来存储我的首选项的键名。
我知道这是一个好主意,因为如果需要的话,它可以很容易地更改密钥。 另外,它是整个“从你的逻辑分离你的数据”的概念。
无论如何,是否有一个很好的方法来使整个应用程序定义一次这些常量? 我相信这是一种简单而明智的方式,但现在我的课程正在重新定义他们使用的课程。
你应该像创build一个头文件
// 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应该被添加到你的应用程序/框架的目标,以便它被链接到最终产品。
使用string常量而不是#define
的d常量的优点是,您可以使用比string比较快得多的指针比较( stringInstance == MyFirstConstant
)来testing是否相等( [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";
第二个好处是,改变常量的值不会导致整个程序的重build。
还有一件事要提。 如果你需要一个非全局常量,你应该使用static
关键字。
例
// In your *.m file static NSString * const kNSStringConst = @"const value";
由于static
关键字,这个const在文件之外是不可见的。
微调@QuinnTaylor :静态variables在编译单元中是可见的。 通常,这是一个单一的.m文件(就像这个例子中的那样),但是如果你把它声明在其他地方包含的头文件中,它可能会咬你,因为在编译之后你会得到链接器错误
接受的(和正确的)答案说:“你可以在项目的预编译头文件中包含这个[Constants.h]文件…”。
作为一个新手,我没有做进一步的解释 – 难度如何:在YourAppNameHere-Prefix.pch文件中(这是Xcode中预编译头文件的默认名称), 在#ifdef __OBJC__
块中导入Constants.h 。
#ifdef __OBJC__ #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #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预处理器。 让我通过这个例子来解释。
我有一个头文件,它定义macrosSTR_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的build议,以便在常量头文件包含在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"); …
有几件事要注意:
-
在
#undef
ing 之后,我不得不在.m
文件中重新定义macros,以便使用macros。 -
我还必须使用
#include
而不是#import
来正常工作,避免编译器看到以前的预编译值。 -
这将需要重新编译你的PCH(可能是整个项目),只要任何值被改变,如果它们被正常分离(和复制),情况就不是这样。
希望对某人有帮助。
我自己有一个标题专门用于声明常量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";
这种方法给了我很好的帮助。
编辑:请注意,如果string在多个文件中使用,这最好。 如果只有一个文件使用它,则可以在使用该string的.m文件中执行#define kNSStringConstant @"Constant NSString"
。
// Prefs.h extern NSString * const RAHUL; // Prefs.m NSString * const RAHUL = @"rahul";
正如Abizer所说,你可以把它放到PCH文件中。 另一种不那么脏的方法是为所有密钥创build一个包含文件,然后将其包含在您使用密钥的文件中,或者将其包含在PCH中。 用他们自己的包含文件,至less给你一个地方去寻找和定义所有这些常量。
如果你想要像全局常量的东西, 一个肮脏的方法是将常量声明放入pch
文件中。
我使用单例类,以便我可以嘲笑类,并在必要时更改常量以进行testing。 常量类看起来像这样:
#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的简写forms – 每次都节省了input[[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
尝试使用类方法:
+(NSString*)theMainTitle { return @"Hello World"; }
我有时使用它。
如果你喜欢命名空间常量,你可以利用struct, Friday Q&A 2011-08-19:命名空间常量和函数
// 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" };