编写高质量的 iOS 和 OS X 代码的52个有效办法-读书笔记
最近在读Effective Objective-C 2.0
,想做一下记录,我是挑选自己感兴趣的,和书中的顺序不一样,看到那写到哪
第4条:多用类型常量,少用 #define 预处理命令
在定义常量的时候,我们通常需要确定一下几件事:
常量代表的含义
常量的作用域
什么样的常量的命名方式简单易懂?
如果我们定义个动画时间的时候,我们通常会这么定义
#define ANIMATION_DURATION 0.3
这么定义的缺点是
-
常量代表的含义
不清晰,未明确指出0.3与时间有关. -
常量的作用域
过大,预处理过程会把碰到所有的ANIMATION_DURATION
一律替换成0.3,这样的话,假设此指令声明在某个头文件中,那么引入这个头文件的代码中的ANIMATION_DURATION
都会被替换掉,所以说即使你使用#define定义常量,也要放在.m
文件里 -
什么样的常量的命名方式简单易懂?
,这种命名方式,没有规范,下面会介绍一下规范的定义常量
正确的定义方式应该按照下面两种方式:
第一,常量的作用域仅在当前类,即局部常量
//in the implementation file - EOCAnimatedView.m
static const NSTimeInterval kAnimationDuration = 0.3;
第二,常量的作用域在所有引用了当前类的代码中,即全局常量
//in the header file - EOCAnimatedView.h
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
//in the implementation file - EOCAnimatedView.m
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;
为什么要这样定义呢?
-
常量代表的含义
明确,0.3与时间有关,所以我们用NSTimeInterval
表示,这样容易让人理解 -
常量的作用域
明确,常量的作用域仅在当前类时,我们用第一种方法定义,常量的作用域在所有引用了当前类的代码中,即全局变量
我们用第二种方式定义 -
什么样的常量的命名方式简单易懂?
若某常量局限于编译单元(translation unit)
也就是"实现文件"即.m
文件,则在前面加字母k
,因为局部变量
仅在当前类使用,所以不用加以区分,若常量在类外可见,则通常以类名为前缀,因为全局常量
加入到全局常量表
中,必须用类名加以区分
为什么我们不能在.h文件里面定义局部常量
?
局部常量
的定义不应该出现在.h
文件里,因为 OC 没有 namespace 这个概念,所有引入这份头文件的其他文件都会出现这个名字,如果在局部常量
的定义出现在.h
文件里,就等于声明了一个kAnimationDuration
的全局常量
为什么常量的作用域仅在当前类时,我们用第一种方法定义?
因为static
修饰符意味着该常量仅定义在定义该常量的编译单元中(.m)
可见, 编译器每收到一个编译单元,就会输出一份目标文件(object file)
,如声明此常量不加static
,编译器会为它创建一个外部符号(external symbol)
, 此时,另一个编译也声明了同名变量,那么编译器就会报错.
为什么常量的作用域在所有引用了当前类的代码中,即全局变量
我们用第二种方式定义?
全局常量
需要放在全局符号表(global symbol table)
中,这种常量的定义是在头文件声明
,在实现文件里定义
,这样,编译器在使用此常量的时候无需查看其定义,即允许代码使用此常量,因为他知道,当链接成二进制文件之后,肯定能找到这个常量.
Note:
- 常量的定义应该从右到左解读
- 使用
第二种定义
优于使用#define
,因为#define
定义出来的常量不含类型信息,编译器只会在编译前据此执行查找与替换操作,即使有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量值不一致