Objective-C 中的常量

发布于 2024-07-13 11:04:26 字数 343 浏览 7 评论 0原文

我正在开发一个 Cocoa 应用程序,并且我正在使用常量 NSString 是根据我的喜好存储键名的方法。

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

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

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

I'm developing a Cocoa application, and I'm using constant NSStrings as ways to store key names for my preferences.

I understand this is a good idea because it allows easy changing of keys if necessary.
Plus, it's the whole 'separate your data from your logic' notion.

Anyway, is there a good way to make these constants defined once for the whole application?

I'm sure that there's an easy and intelligent way, but right now my classes just redefine the ones they use.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

友谊不毕业 2024-07-20 11:04:26

应该创建一个像这样的头文件:(

// 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:MyF​​irstConstant]) (并且更易于阅读,IMO)。

You should create a header file like:

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

(You can use extern instead of FOUNDATION_EXPORT if your code will not be used in mixed C/C++ environments or on other platforms.)

You can include this file in each file that uses the constants or in the pre-compiled header for the project.

You define these constants in a .m file like:

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

Constants.m should be added to your application/framework's target so that it is linked in to the final product.

The advantage of using string constants instead of #define'd constants is that you can test for equality using pointer comparison (stringInstance == MyFirstConstant) which is much faster than string comparison ([stringInstance isEqualToString:MyFirstConstant]) (and easier to read, IMO).

怎会甘心 2024-07-20 11:04:26

最简单的方法:

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

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

Easiest way:

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

Better way:

// Prefs.h
extern NSString * const PREFS_MY_CONSTANT;

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

One benefit of the second is that changing the value of a constant does not cause a rebuild of your entire program.

笑梦风尘 2024-07-20 11:04:26

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

示例

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

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


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

There is also one thing to mention. If you need a non global constant, you should use static keyword.

Example

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

Because of the static keyword, this const is not visible outside of the file.


Minor correction by @QuinnTaylor: static variables are visible within a compilation unit. Usually, this is a single .m file (as in this example), but it can bite you if you declare it in a header which is included elsewhere, since you'll get linker errors after compilation

刘备忘录 2024-07-20 11:04:26

接受的(正确的)答案是“您可以包含这个 [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 文件中绝对不应包含任何其他内容。 (没有接口或实现)。

The accepted (and correct) answer says that "you can include this [Constants.h] file... in the pre-compiled header for the project."

As a novice, I had difficulty doing this without further explanation -- here's how: In your YourAppNameHere-Prefix.pch file (this is the default name for the precompiled header in Xcode), import your Constants.h inside the #ifdef __OBJC__ block.

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

Also note that the Constants.h and Constants.m files should contain absolutely nothing else in them except what is described in the accepted answer. (No interface or implementation).

贱贱哒 2024-07-20 11:04:26

我通常使用 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"

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

I am generally using the way posted by Barry Wark and Rahul Gupta.

Although, I do not like repeating the same words in both .h and .m file.
Note, that in the following example the line is almost identical in both files:

// file.h
extern NSString* const MyConst;

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

Therefore, what I like to do is to use some C preprocessor machinery.
Let me explain through the example.

I have a header file which defines the macro 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

The in my .h/.m pair where I want to define the constant I do the following:

// myfile.h
#import <StringConsts.h>

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

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

et voila, I have all the information about the constants in .h file only.

久随 2024-07-20 11:04:26

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

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" 即可。

I myself have a header dedicated to declaring constant NSStrings used for preferences like so:

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

Then declaring them in the accompanying .m file:

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

This approach has served me well.

Edit: Note that this works best if the strings are used in multiple files. If only one file uses it, you can just do #define kNSStringConstant @"Constant NSString" in the .m file that uses the string.

困倦 2024-07-20 11:04:26

对@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.h
STR_CONST(kMyConstant, "Value");
…

需要注意的几件事:

  1. 我必须在.m文件之后重新定义宏#undef将其设置为要使用的宏。

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

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

希望这对某人有帮助。

A slight modification of the suggestion of @Krizz, so that it works properly if the constants header file is to be included in the PCH, which is rather normal. Since the original is imported into the PCH, it won't reload it into the .m file and thus you get no symbols and the linker is unhappy.

However, the following modification allows it to work. It's a bit convoluted, but it works.

You'll need 3 files, .h file which has the constant definitions, the .h file and the .m file, I'll use ConstantList.h, Constants.h and Constants.m, respectively. the contents of Constants.h are simply:

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

and the Constants.m file looks like:

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

Finally, the ConstantList.h file has the actual declarations in it and that is all:

// ConstantList.h
STR_CONST(kMyConstant, "Value");
…

A couple of things to note:

  1. I had to redefine the macro in the .m file after #undefing it for the macro to be used.

  2. I also had to use #include instead of #import for this to work properly and avoid the compiler seeing the previously precompiled values.

  3. This will require a recompile of your PCH (and probably the entire project) whenever any values are changed, which is not the case if they are separated (and duplicated) as normal.

Hope that is helpful for someone.

仲春光 2024-07-20 11:04:26
// Prefs.h
extern NSString * const RAHUL;

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

// Prefs.m
NSString * const RAHUL = @"rahul";
浪漫之都 2024-07-20 11:04:26

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

As Abizer said, you could put it into the PCH file. Another way that isn't so dirty is to make a include file for all of your keys and then either include that in the file you're using the keys in, or, include it in the PCH. With them in their own include file, that at least gives you one place to look for and define all of these constants.

心作怪 2024-07-20 11:04:26

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

If you want something like global constants; a quick an dirty way is to put the constant declarations into the pch file.

南薇 2024-07-20 11:04:26

如果您喜欢命名空间常量,则可以利用结构, 星期五问答 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"
};

If you like namespace constant, you can leverage 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"
};
笑饮青盏花 2024-07-20 11:04:26

尝试使用类方法:

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

我有时会使用它。

Try using a class method:

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

I use it sometimes.

往事随风而去 2024-07-20 11:04:26

我使用单例类,以便我可以模拟该类并在测试需要时更改常量。 常量类看起来像这样:

#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

I use a singleton class, so that I can mock the class and change the constants if necessary for testing. The constants class looks like this:

#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

And it is used like this (note the use of a shorthand for the constants c - it saves typing [[Constants alloc] init] every time):

#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
情未る 2024-07-20 11:04:26

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

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

你有很好的可读常量定义,并且可从您选择的类型中使用,同时又受类型上下文的限制。

If you want to call something like this NSString.newLine; from objective c, and you want it to be static constant, you can create something like this in swift:

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

And you have nice readable constant definition, and available from within a type of your choice while stile bounded to context of type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文