编写代码时经常要定义常量。
比如:
#define ANIMATION_DURATION 0.3
上述预处理指令会把源码中的 ANIMATION_DURATION 字符串替换成 0.3 。
不过这样定义出来的常量没有类型
尽量避免在头文件里声明预处理指令
比用预处理指令来定义常量更好的方式:
static const NSTimeInterval kAnimationDuration = 0.3;
用此方式定义的常量包含类型信息,其好处是清楚的描述了常量的含义。由此可知常量的类型为 NSTimeInterval 。
注意常量的名称,若常量局限于某编译单元(.m 实现文件)之内,则在前面加字母k;若常量在类之外可见,则通常以类名为前缀,以表明其所属的类,例如 XZViewClassAnimationDuration。
案例:
// XZAnimatedView.h
#import <UIKit / UIKit.h>
@interface XZAnimatedView : UIView
- (void)animate;
@end
//XZAnimatedView.m
#import "XZAnimatedView.h"
static const NSTimeInterval kAnimationDuration = 0.3;
@implementation XZAnimatedView
- (void)animate {
[UIView animateWithDuration:kAnimationDuration
animations:(^) {
//perform animation
}];
}
@end
变量一定要同时使用 static 与 const 来声明。如果试图修改由 const 修饰符所声明的变量,那么编译器就会报错。static修饰符表示该变量只在定义此变量的编译单元(每个类的实现文件 .m 后缀名)中可见。
编译器每收到一个编译单元,就会输出一份“目标文件”(object file)。上述代码中声明的 kAnimationDuration 变量,其作用域仅限于由 XZAnimatedView 生成的目标文件中。假如声明此变量时不加 static ,那么编译器会为它创建一个“外部符号”(external symbol)。此时,若其他编译单元也声明了同名变量,编译器就会抛出一条错误信息:
duplicate symbol _kAnimationDuration in :
XZAnimatedView.o
XZOtherView.o
实际上,一个变量既声明为 static , 又声明为 const ,那么编译器根本就不会创建符号,而是会像 #define 预处理指令一样,把所有遇到的变量都替换为常量。不过还是要记住:用这种方式定义的常量带有类型信息。
有时候需要对外公开某个常量。比如说,你可能要在类代码中调用 NSNotificationCenter 以通知他人。用一个对象来派发通知(派发通知者声明通知),令其他欲接收通知的对象向该对象注册(接收者添加为该通知的观察者),这样就能实现此功能。派发通知时,需要使用字符串来表示此项通知的名称,而这个名字就可以声明为一个外界可见的常值变量。这样的话,注册者无须知道实际字符串值,只需以常值变量来注册自己想要接收的通知即可。
此类常量需放在“全局符号表”中,以便可以在定义该变量的编译单元之外使用。应该这样来定义:
// In the header file
extern NSString *const XZStringConstant;
// In the implementation file
NSString *const XZStringConstant = @"value";
这在常量在头文件中“声明”,且在实现文件中“定义”。注意 const 修饰符在常量类型中的位置。常量定义应从右向左解读,XZStringConstant 就是“一个常量,而这个常量是个指针,指向 NSString 对象。 ”
编译器看到头文件中的 extern 关键字,就能明白如何在引入此头文件的代码中处理该常量了。这个关键字是要告诉编译器,在全局符号表中将有一个名叫 XZStringConstant 的符号。
此类常量必须要定义,且只能定义一次,通常定义在与声明该常量的头文件相关的实现文件中。
因为符号要放在全局符号表里,所以命名常量时一定要谨慎。
例如:某应用程序中有个处理登录操作的类,在登录完成后会发出通知。派发通知所用的代码如下:
//XZLoginManager.h
#import <Foundation/Foundation.h>
extern NSString *const XZLoginManagerDidLoginNotification;
@interface XZLoginManager : NSObject
- (void)login;
@end
//XZLoginManager.m
#import "XZLoginManager.h"
NSString *const XZLoginMangerDidLoginNotification = @"XZLoginMangerDidLoginNotification";
@implementation XZLoginManger
- (void)login {
// call p_didLogin.
}
- (void)p_didLogin {
[ [NSNotificationCenter defaultCenter] postNotificationName:XZLoginMangerDidLoginNotification object:nil];
}
@end
注意常量的名字。为避免名称冲突,最好是用与之相关的类名做前缀。系统框架中一般都这么做。例如 UIKit中的 UIApplicationDidEnterBackgroundNotification、UIApplicationWillEnterForegroundNotification。
这样定义常量要优于使用 #define 预处理指令, 因为编译器会确保常量值不变。采用预处理指令定义的常量可能会无意中遭人修改,从而导致应用程序各个部分所使用的值互不相同。
网友评论