Objective-C中的常量

时间:2009-02-11 21:52:03

标签: objective-c cocoa constants

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

我知道这是一个好主意,因为它可以在必要时轻松更改密钥。此外,它是整个'将您的数据与逻辑分离'的概念。

无论如何,是否有一种很好的方法可以为整个应用程序定义一次这些常量?我确信这是一种简单而聪明的方式,但是现在我的课程只是重新定义了他们使用的课程。

14 个答案:

答案 0 :(得分:1271)

您应该创建一个像

这样的头文件
// 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'常量的优点是可以使用指针比较(stringInstance == MyFirstConstant)测试相等性,这比字符串比较([stringInstance isEqualToString:MyFirstConstant])快得多(并且更容易阅读,IMO)。

答案 1 :(得分:274)

最简单的方法:

// 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";

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

答案 2 :(得分:187)

还有一件事需要提及。如果您需要非全局常量,则应使用static关键字。

实施例

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

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


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

答案 3 :(得分:118)

接受的(和正确的)答案说“你可以在项目的预编译标题中包含这个[Constants.h]文件....”

作为一个新手,我在没有进一步解释的情况下很难做到这一点 - 具体如下:在你的YourAppNameHere-Prefix.pch文件中(这是Xcode中预编译头的默认名称),导入你的Constants.h #ifdef __OBJC__区块内

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

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

答案 4 :(得分:50)

我通常使用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
#import <StringConsts.h>

STR_CONST(MyConst, "Lorem Ipsum");
STR_CONST(MyOtherConst, "Hello world");

// myfile.m
#define SYNTHESIZE_CONSTS
#import "myfile.h"

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

答案 5 :(得分:27)

我自己有一个标题专门用于声明用于首选项的常量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"

答案 6 :(得分:25)

稍微修改@Krizz的建议,以便在常规头文件包含在PCH中时它能正常工作,这是很正常的。由于原始文件被导入到PCH中,因此不会将其重新加载到.m文件中,因此您没有符号,链接器也不满意。

但是,以下修改允许它工作。这有点令人费解,但它确实有效。

您需要 3 文件,.h文件具有常量定义,.h文件和.m文件,我将使用{分别为{1}},ConstantList.hConstants.hConstants.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

需要注意几点:

  1. 我必须重新定义// ConstantList.h STR_CONST(kMyConstant, "Value"); … 文件中的宏 .m才能使用该宏。

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

    < / LI>
  3. 这将需要在任何值发生更改时重新编译您的PCH(可能还有整个项目),如果它们正常分开(并重复),则不需要重新编译。

  4. 希望这对某人有帮助。

答案 7 :(得分:14)

// Prefs.h
extern NSString * const RAHUL;

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

答案 8 :(得分:12)

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

答案 9 :(得分:11)

如果你想要像全局常量那样的东西;一种快速的方法是将常量声明放入pch文件中。

答案 10 :(得分:8)

尝试使用类方法:

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

我有时会用它。

答案 11 :(得分:8)

如果您喜欢名称空间常量,则可以使用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"
};

答案 12 :(得分:7)

我使用单例类,因此我可以模拟类并在必要时更改常量以进行测试。常量类看起来像这样:

#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

答案 13 :(得分:0)

如果要从目标c调用类似public extension NSString { @objc public static let newLine = "\n" } 的东西,并且希望它是静态常量,则可以快速创建类似这样的东西:

import {ViewChild} from '@angular/core';

您将拥有很好的可读性常量定义,并且可以在选择的类型中使用,同时还限于类型上下文。