我一直在我的头文件中使用静态const:
static NSString * const myString = @"foo";
但是已经读到这不是'安全'或正确的方法。显然,如果我想从另一个类访问我的const字符串,我应该在我的.h中声明字符串:
extern NSString * const myString;
然后在我的.m文件中:
NSString * const myString = @"foo";
这是对的吗?如果是这样,是什么原因不直接在我的.h文件中将其声明为静态?它工作得很好,我看不到任何“安全”问题。它是一个const,因此它不能从外部改变,而是我故意需要在类之外访问它。我能想到的另一件事是隐藏字符串的值吗?
答案 0 :(得分:35)
您的第一个变体
static NSString * const myString = @"foo"; // In .h file, included by multiple .m files
在每个"翻译单元" 中本地定义myString
变量(粗略地说:在每个.m源文件中)
包括头文件。所有字符串对象都具有相同的内容" foo",
但它可能是不同的对象,以便myString
(指针到字符串对象)的值
每个单元可能有所不同。
你的第二个变种
extern NSString * const myString; // In .h file, included by multiple .m files
NSString * const myString = @"foo"; // In one .m file only
定义了单个变量myString
,该变量可见"全球"。
示例:在一个班级中,您发送一个myString
作为用户对象的通知。
在另一个类中,收到此通知,并将用户对象与myString
进行比较。
在您的第一个版本中,必须使用isEqualToString:
进行比较 ,因为
发送和接收类可能有不同的指针(都指向a
NSString
对象包含内容" foo")。因此,与==
进行比较可能会失败。
在您的第二个版本中,只有一个myString
变量,因此您可以与==
进行比较。
所以第二个变体是更安全,因为"共享字符串"是每个翻译单元中的相同对象。
答案 1 :(得分:3)
我没有理由在Objective-C中声明任何外部内容,而只在项目中使用Objective-C。 当你将它与C或汇编程序模块等混合时,我可以想到原因。
然而,extern
的优点是,如果你真的需要保存这20个左右的字节,那么常量将只存在于整个项目中的那个,如果它是你想要实现的那个。
但这带来了名称冲突的风险。其他库可能使用相同的名称声明了自己的外部。并且链接器会使用内存中的相同空间来关注它们,尽管它们可能是不同类型的。
是的,标题中的extern
声明应该附带.m文件中的相应定义。我不确定,但我认为你可以分配@" foo"在.h文件中已经存在。
您甚至可以在@interface / @ implementation- @ end blocks之外声明它。 (从未尝试过我自己)。在这种情况下,即使没有extern
关键字,变量也是全局的,可以从任何地方访问。在编译时,编译器会在#include语句链中看不到它的声明时会抱怨访问它们。但在学术上,一个单独的.m文件可以包含两个或更多个类(我显然不建议),然后变量可以从两个类访问,尽管它们都不属于它们。
最后,OjbC只是ANSI C的附加组件。
然而,使它们静止没有意义。无论如何,这些常量都是静态的。它们是常数。类或甚至方法中静态变量的目的是它的范围(可见性)仅限于该类,但运行时只有一个实例由该类的所有实例共享。
示例:
@implementation AClass : NSObject
static NSString *someString
- (void) setString:(NSString*) aString{
someString = aString;
}
- (NSString*) getString (){
return someString;
}
......以及其他地方:
AClass * a = [[AClass alloc] init];
AClass * b = [[AClass alloc] init];
[a setString:@"Me"];
[b setString;@"You"];
NSLog (@"String of a: ", [a getString]);
会打印You
但不打印Me
如果这是你想要的,那么,只使用静态。
使用简单的预处理器宏(我更喜欢,但我在这里有点老了)的缺点是每次使用宏时这些字符串都会被复制到二进制文件中。显然这对你来说不是一个选择,因为你甚至没有要求他们。 但是,对于大多数用法,通常共享的.h文件中的预处理器宏可以实现跨类管理常量的技巧。
答案 2 :(得分:1)
在标头文件中使用static NSString* const myString = @"foo";
表示每个翻译单元都会获得单独的myString
变量。我认为链接器可以合并这些,但我不会指望它。这意味着,如果调用者来自不同的翻译单元,那么即使调用者通过了if (someString == myString) ...
,使用myString
收到的字符串的代码也可能会变为假。 (当然,代码应该使用-isEqualToString:
而不是==
,但使用正确声明的字符串常量,后者可能是可行的。)
答案 3 :(得分:1)
外部方式的优点在其他答案中有所描述。
如果变量不是一个对象(在你的情况下为NSString
)而是一个原始类型(如整数)的静态方式的一个可能的优点是,编译器是能够优化对常量的访问:
在程序中声明const时,
int const x = 2;
编译器可以通过不为此变量提供存储来优化掉这个const,而是将其添加到符号表中。因此,后续读取只需要间接到符号表而不是从内存中获取值的指令。
取自this answer。
答案 4 :(得分:0)
对于存储类,静态意味着两件事之一。
方法或函数内的静态变量保留其值 在调用之间。
可以调用全局声明的静态变量 通过任何功能或方法,只要这些功能出现在 与静态变量相同的文件。静态函数也是如此。
静态使函数和变量在特定文件中全局可见,而extern使它们对所有文件全局可见。
如果您的应用程序在公共接口中使用带有非语言值的字符串常量,则应将其声明为外部字符串常量。
模式是在公共头中声明一个extern NSString * const,并在实现中定义NSString * const。
答案 5 :(得分:0)
但是已经读到这不是'安全'或正确警惕这样做。
除非程序是多线程的,否则它是安全的。在这种情况下,除非使用互斥锁保护全局变量,否则它是不安全的。
然而这不正确。 NSString * const myString
表示指向(非常量)数据的常量指针。最有可能的是,您希望变量本身保持不变:const NSString* myString
。
显然如果我想从另一个类访问我的const字符串,我应该在我的.h中声明字符串:
extern
...
正确。请注意,extern
仅适用于常量。非常数变量是不可接受的:全局变量被认为是非常糟糕的实践。
如果是这样的话,不仅仅是在我的.h文件中使用声明为静态的原因是什么,因为它完全正常
您希望将其声明为静态的唯一原因是您希望将变量的范围限制为本地.c文件,换言之,私有封装。因此,在.h文件中声明静态变量是没有意义的。
这是一个const所以因此不能从外面改变
可以改变,因为它不是常量,请参阅我的第一句话。
一般来说,不要做这样的事情。所有上述摆弄表明你的程序设计存在缺陷,需要修复。您需要以面向对象的方式设计程序,以便每个代码模块都是自治的,并且除了指定的任务之外不会知道或关心任何事情。
答案 6 :(得分:-1)
#define myString @"foo"
有例子:
您将能够在编译时连接字符串:
NSLog(@"%@", @"Say hello to " myString);
将输出:向foo打个招呼