我一直在寻找常量,除了不能以编程方式进行更改之外,我并不了解它们的不同之处。
extern NSString * const MyConstant;
有了这一行,extern
究竟是什么意思,const
究竟是什么意思?
答案 0 :(得分:19)
这里有两个问题:一个关于常数,一个关于外部问题。这两者不一定相关。
首先, const :正如你所说的那样,常量并不多,它们不能以编程方式进行更改。不同的东西可以是不变的,这取决于你如何声明它们。例如,在您的示例中:
NSString * const MyConstant = @"foo";
你已经声明了一个指向非常量NSString对象的常量指针; const
关键字位于星号右侧,因此它指向指针。因此,这个:
MyConstant = @"bar";
会导致编译错误,因为它试图重新分配MyConstant
以指向不同的NSString。
如果const
关键字位于星号的左侧,则它将引用指针引用的对象(在本例中为底层NSString结构)。这可能不是你在Objective C中大多数时候想要的。注意const
关键字相对于类型标识符的位置并不重要,所以这个:
const NSString *MyConstant = @"foo";
和此:
NSString const *MyConstant = @"foo";
意思是同样的事情。您还可以合法地声明指针和引用的值const,以获得最大的常量:
const NSString * const MyConstant = @"foo";
其次, extern :extern
只允许您在一个编译单元中声明一个变量,并让编译器知道您已在单独的编译单元中定义了该变量。您通常只将此用于全局值和常量。
您可以将编译单元视为单个.m
文件,以及它包含的所有.h
文件。在构建时,编译器将每个.m文件编译为单独的.o
文件,然后链接器将它们全部挂钩到一个二进制文件中。通常,一个编译单元知道在另一个编译单元中声明的标识符(例如类名)的方式是通过导入头文件。但是,对于全局变量,它们通常不是类的公共接口的一部分,因此它们经常在.m
文件中声明和定义。
如果编译单元A在.m
文件中声明全局:
#import "A.h"
NSString *someGlobalValue;
和编译单元B想要使用全局:
#import "B.h"
extern NSString *someGlobalValue;
@implementation B
- (void)someFunc {
NSString *localValue = [self getSomeValue];
if (localValue isEqualToString:someGlobalValue]) {
...
}
}
单元B必须以某种方式告诉编译器使用单元A声明的变量。它不能导入声明发生的.m
文件,所以它使用extern
告诉编译器变量存在于其他地方。
请注意,如果单元A和单元B 两者都将此行放在文件的顶层:
NSString *someGlobalValue;
然后你有两个声明相同全局变量的编译单元,链接器将失败并出现重复的符号错误。如果你想拥有一个这样的变量,它在编译单元中只存在 ,并且对任何其他编译单元都是不可见的(即使它们使用extern
),你可以使用{{ 1}}关键字:
static
这对于您希望在单个实现文件中使用的常量非常有用,但在其他地方不需要。
答案 1 :(得分:2)
extern NSString * const MyConstant;
你会在头文件中看到这个。它告诉编译器变量MyConstant
存在并且可以在您的实现文件中使用。
更可能的是,变量设置如下:
NSString * const MyConstant = @"foo";
该值无法更改。如果您想要一个可以更改的全局,请从声明中删除const
。
答案 2 :(得分:-1)
Extern意味着变量设置在声明它的源文件之外。建议使用OBJC_EXPORT标志而不是extern。
Const表示设置后无法更改变量。但是,您可以使用一些指针误导,如下所示:
NSString **var = (NSString **)&MyConstant;
*var = @"I changed it!";
如果您需要(例如,在您的班级+initialize
方法中),这实际上允许您更改它。