编写代码经常会用到常量,如:
#define AB_LANGUAGE_ZH 1 #define AB_LANGUAGE_EN 2 #define AB_LANGUAGE_AR 3 #define AB_LANGUAGE_DE 4这是之前我们代码中定义的常量,只从名字根本不值得这是什么类型的数据,只知道大概和语言有关系。
又如:将ui视图中播放动画的时间提取为常量,我们经常会这样做(这样定义的很方便):
#define ANIMATION_DURATION 0.5该预处理指令会把代码中所有ANIMATION_DURATION替换为0.5。这可能是我们想要的效果,不过这样定义的常量 没有类型信息。假设此指令声明在某个 头文件中,那么所有引入了这个头文件的代码,其 ANIMATION_DURATION都会被替换。
static const NSTimeInterval kAnimationDuration 0.5此方式定义的常量包含 类型信息,很清楚的描述了常量的含义。
还要注意常量名称。常用的命名法是:
若常量局限于“编译单元”(也即是“实现文件”)之内,则在前面加字母k;
若常量在类之外可见,则通常以类名为前缀。
定义位置
定义常量的位置很重要。我们总是喜欢在头文件里声明预处理指令,这样做真的很糟糕,当常量名称有可能互相冲突时更是如此。
例如,ANIMATION_DURATION这个常量名就不该用在头文件中,因为所有引入了这份头文件的其他文件中都会出现这个名字。其实就连用satic const定义的变量也不应在头文件中,如果这样就等于声明了一个叫kAnimationDuration的全局变量。
若不打算公开某常量,则应将其定义在使用该常量的实现文件里。例如:
// // EOCAnimatedView.h // #import <UIKit/UIKit.h> @interface EOCAnimatedView : UIView @end // // EOCAnimatedView.m // #import "EOCAnimatedView.h" static const NSTimeInterval kAnimataionDuataion = 0.5; @implementation EOCAnimatedView @end
而static修饰符则意味着该变量仅在定义此变量的编译单元(在OC环境下,“编译单元”通常指某个类的实现文件)中可见。编译器没收到一个编译单元,就会输出一份“目标文件”,因此kAnimationDuration变量,其作用域仅限于由EOCAnimatedVIew所生成的目标文件。假如声明此变量不加static,编译器会为它创建一个“外部符号”。此时若另一个编译单元也声明了同名变量,那么编译器会抛错误:
duplicate symbol _kAnimationDuration in: EOCAnimatedView.o EOCOtherView.o如果一个变量既声明为static,也声明为const,那么编译器根本不会创建符号,而是像#define预处理指令一样,把所有kAnimationDuration 都替换为常值。
如果打算公开某个常量。如:在类代码中调用NSNotificationCenter通知其他类,用一个对象来派发通知,。派发通知时,需要使用字符串来表示此项通知的名称,这个名字就可以声明为一个外界可见的常值变量。这样,注册者无须知道实际字符串值,只需以常值变量来注册资金想要接收的通知即可。
定义:
//In the header file extern NSString *const EOCStringConstant; //In the implementation file NSString *const EOCStringConstant = @"VALUE";注意 const修饰符在常量类型中的位置。常量定义应从右至左解读。EOCStringConstant就是“一个常量,而这个常量是指针,指向NSString对象”。
编译器看到头文件中的extern关键字,就能明白如何在引入头文件的代码中处理该常量了。这个关键字是要告诉编译器,在全局符符号表中将会有一个名叫EOCStringConstant的符号。也就是会所,编译器无须查看其定义,即允许代码使用此常量。因为它知道,当链接成二进制文件后,肯定能找到这个常量。
因为符号要放在全局符号表中,所以命名时需谨慎。为避免名称冲突,最好用与之相关的类名做前缀。系统框架一般都这样做。如:UIKit中,
UIApplicationDidEnterBackgroundNotification
UIApplicationWillEnterForegroundNotification。
1 不要用预处理指令定义常量。这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换操作。即使有人重现定义了常量值,编译器也不会产生警告⚠️,这将导致应用程序中的常量值不一致。
2 在实现文件中使用static const来定义“只在编译单元内可见的常量”。由于此类常量不在全局符号表中,所以无须为其名称加前缀。
3 在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以其名称应加以区隔,通常用与之相关的类名做前缀。