提问人:Allyn 提问时间:2/12/2009 最后编辑:Arsen KhachaturyanAllyn 更新时间:1/21/2022 访问量:446396
Objective-C 中的常量
Constants in Objective-C
问:
我正在开发一个 Cocoa 应用程序,我正在使用常量 s 来存储我的偏好的键名称。NSString
我知道这是一个好主意,因为它允许在必要时轻松更改密钥。
此外,这是整个“将数据与逻辑分离”的概念。
无论如何,有没有一种好方法可以为整个应用程序定义一次这些常量?
我确信有一种简单而智能的方法,但现在我的类只是重新定义了它们使用的类。
答:
如果你想要像全局常量这样的东西;一种快速而肮脏的方法是将常量声明放入文件中。pch
评论
最简单的方法:
// 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";
第二个好处是,更改常量的值不会导致整个程序的重建。
评论
extern NSString const * const MyConstant
正如 Abizer 所说,您可以将其放入 PCH 文件中。另一种不那么肮脏的方法是为所有密钥创建一个包含文件,然后将其包含在使用密钥的文件中,或者将其包含在 PCH 中。将它们放在自己的包含文件中,这至少为您提供了一个查找和定义所有这些常量的位置。
您应该创建一个头文件,如下所示:
// 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
应该添加到应用程序/框架的目标中,以便将其链接到最终产品。
使用字符串常量而不是 'd 常量的优点是,您可以使用指针比较 () 来测试相等性,这比字符串比较 () 快得多(并且更易于阅读,IMO)。#define
stringInstance == MyFirstConstant
[stringInstance isEqualToString:MyFirstConstant]
评论
NSString
copy
retain
NSString*
-isEqualToString:
还有一件事要提。如果需要非全局常量,则应使用 keyword。static
例
// In your *.m file
static NSString * const kNSStringConst = @"const value";
由于关键字的原因,此常量在文件外部不可见。static
@QuinnTaylor的微小校正:静态变量在编译单元中是可见的。通常,这是一个单独的 .m 文件(如本例所示),但如果您在其他地方包含的标头中声明它,它可能会咬您,因为编译后会出现链接器错误
评论
尝试使用类方法:
+(NSString*)theMainTitle
{
return @"Hello World";
}
我有时会使用它。
评论
isEqualToString:
公认的(和正确的)答案是“你可以包括这个 [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 文件除了已接受的答案中描述的内容外,绝对不包含任何其他内容。(无接口或实现)。
评论
// Prefs.h
extern NSString * const RAHUL;
// Prefs.m
NSString * const RAHUL = @"rahul";
我通常使用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 文件中拥有有关常量的所有信息。
评论
对 @Krizz 的建议稍作修改,以便在常量头文件包含在 PCH 中时正常工作,这是相当正常的。由于原始文件已导入 PCH,因此它不会将其重新加载到文件中,因此您不会获得任何符号,并且链接器不满意。.m
但是,以下修改允许它工作。这有点复杂,但它有效。
您将需要 3 个文件,具有常量定义的文件,file 和 file,我将分别使用 和 。内容简单来说就是:.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");
…
有几点需要注意:
我必须在 ing 之后重新定义文件中的宏才能使用宏。
.m
#undef
我还必须使用
#include
而不是让它正常工作,并避免编译器看到先前预编译的值。#import
每当任何值发生更改时,这都需要重新编译您的 PCH(可能还有整个项目),如果它们像往常一样分开(和复制),则情况并非如此。
希望这对某人有所帮助。
评论
extern
FOUNDATION_EXPORT
我使用单例类,这样我就可以模拟该类并在必要时更改常量以进行测试。常量类如下所示:
#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"
如果你喜欢命名空间常量,你可以利用结构, 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"
};
评论
__unsafe_unretained
如果你想从目标 c 调用这样的东西,并且你希望它是静态常量,你可以在 swift 中创建这样的东西:NSString.newLine;
public extension NSString {
@objc public static let newLine = "\n"
}
而且你有很好的可读常量定义,并且可以从你选择的类型中获取,同时 stile 绑定到类型的上下文。
评论