我想在objective-c中定义一个常量。
以前我有以下功能:
+(NSString *) getDocumentsDir {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex: 0];
paths = nil;
return documentsDir;
}
我只想定义一个常量“Documents_Dir” - 当函数被调用时,之后才能访问以前创建的值。
我尝试过以下代码,但无效:
#define getDocumentsDir \
{ \
#ifdef Documents_Dir \
return Documents_Dir; \
#else \
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); \
NSString *documentsDir = [paths objectAtIndex: 0]; \
#define Documents_Dir [paths objectAtIndex: 0]; \
paths = nil; \
return Documents_Dir; \
#endif \
} \
我对预编译器指令不太满意,所以任何帮助都将受到赞赏。
答案 0 :(得分:34)
序言:理解预编译器指令和真实常量之间的区别是值得的。 #define
只是在编译器构建代码之前进行文本替换。这对于数值常量和typedef很有用,但对于函数或方法调用并不总是最好的想法。我假设你真的想要一个真正的常量,这意味着创建搜索路径的代码应该只执行一次。
在 MyClass.m 文件中,定义变量并将其填入 +initialize
方法,如下所示:
static NSArray *documentsDir;
@implementation MyClass
+ (void) initialize {
if (documentsDir == nil) {
documentsDir = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES) lastObject] retain];
}
}
...
@end
static
修饰符使其仅在声明它的编译单元中可见。对于一个简单的常数,这就是你所需要的。
如果类具有子类,则每个子类将调用+initialize
一次(默认情况下),因此在分配给documentsDir
之前,您需要检查nil
是否为==
,所以你不要泄漏内存。 (或者,正如Peter Lewis指出的那样,您可以使用extern
或-isMemberOfClass:
方法检查当前正在初始化的类是否为MyClass。)如果子类还需要直接访问常量,您需要在 MyClass.h 文件(子类包含的)中将变量预先声明为extern NSArray *documentsDir;
@interface MyClass : NSObject
...
@end
:
static
如果您将变量预先声明为extern,则必须从定义中删除extern
关键字以避免编译错误。这是必要的,因此变量可以跨越多个编译单元。 (啊,C的乐趣......)
注意:在Objective-C代码中,将某些内容声明为OBJC_EXPORT
的更好方法是使用 #define
(a {{1在<objc/objc-api.h>
)中声明的,它是根据您是否使用C ++设置的。只需将extern
替换为OBJC_EXPORT
即可。
编辑:我刚刚遇到related SO question。
答案 1 :(得分:12)
最简单的解决方案是将路径更改为静态变量,并仅将其评估一次,如下所示:
+(NSString *) getDocumentsDir {
static NSString *documentsDir = nil;
if ( !documentsDir ) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
documentsDir = [paths objectAtIndex: 0];
}
return documentsDir;
}
“static”告诉编译器documentsDir实际上是一个全局变量,尽管只能在函数内访问。因此它被初始化为nil,并且第一次调用getDocumentsDir将重新评估它,然后进一步调用将返回预先评估的值。
答案 2 :(得分:1)
关于Peter N Lewis代码的小优化:
-(NSString *) documentsDir {
static NSString *documentsDir = nil;
return documentsDir ?: (documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]);
}