程序员编写代码时经常要定义常量。例如,要写一个UI视图类,此视图显示出来之后就播放动画,然后消失。你可能想把播放动画的时间提取为常量。掌握了OC与c语言基础的人,也许会经常用这种方法来做:
define ANIMATION_DURATION 0.3
上述预处理会把源码中的ANIMATION_DURATION字符串替换为0.3。这可能就是你想要的效果。不过这样的定义出来的常量没有类型信息。此外,预处理过程会把碰到所有ANIMATION_DURATION一律替换成0.3,这样的话,假设此指令生命在某个头文件中,那么所有引入这个头文件的代码,其ANIMATION_DURATION都会被替换。
那么用什么方法解决呢? 比方说,下面这行代码就可以:
static const NSTimeInterval kAninationDuration = 0.3
请注意用此方法定义的常量包含此类信息,其好处是清楚描述了常量的含义。由此可知常量类型为NSTimeInterval,这有助于理解代码。
若不想公开这个常量则一般将常量定义在实现文件中。
还要注意常量名称。常用的命名法是:
若常量局限于某“编译单元”(translation unit,也就是“实现文件”implementation file)之内,则在前面加上字母k;
若常量在类之外可见,则通常以类名为前缀。
变量一定要同时用static和const来声明。如果试图修改有const修饰的变量,那么编译器就会报错。达到我们不想播放时间被修改的需求。而static修饰符则意味着该变量仅在定义变量的编译单元可见。因此,上例的kAnimationDuration的作用域只在声明此变量.m文件。编译器每收到一个编译单元,就会输出一份“目标文件”(object file)。那么如果声明上述变量不加static修饰,则编译器会为他创建一个“外部符号”(external symbol)。此时若是另一个编译单元也声明了同名变量,那么编译器就会报错。
实际上,如果变量既声明为static又声明为const,那么编译器根本不会创建符号,而是会像#define预处理指令一样,把所有遇到的变量都替换成常值。不过这种定义方法的常量带有类型信息。
那么有时候如果需要公开常量的时候。那么此类常量需要放在“全局符号表”(global symbol table)中,以便可以在定义该常量的编译单元之外使用。因此,其定义方式与上例演示的static const有所不同,应该如下定义,
// In the header file
extern NSString *const EOCStringConstant;
//In the implementation file
NSString *const EOCStringConstant = @"VALUE";
这个变量声明在头文件,且在实现文件“定义”。注意const修饰符在常量类型中的位置。常量定义应该从右至左阅读,所以,EOCStringConstant就是“一个常量,而这个常量是指针,只想NSString对象”,这与需求相符。
要点
1)不要用预处理指令定义常量,这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找和替换,即时有人重新定义此常量值,编译器也不会警告,这将导致应用内常量值不一致。
2)在实现文件使用 static const 定以“只在编译单元可见的常量”,由于此类常量不在全局符号表中,所以无须为其添加前缀。
3)在头文件使用extern来声明全局变量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以其名称应加以区隔,通常与之相关的类名做前缀。