Effective OC 笔记 4多用类型常量,少用#define预处理指令

来源:互联网 发布:高维稀疏数据 编辑:程序博客网 时间:2024/06/07 22:10

1 定义常量

编写代码经常会用到常量,如:

#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的全局变量。

2 不公开常量

若不打算公开某常量,则应将其定义在使用该常量的实现文件里。例如:

////  EOCAnimatedView.h//#import <UIKit/UIKit.h>@interface EOCAnimatedView : UIView@end////  EOCAnimatedView.m//#import "EOCAnimatedView.h"static const NSTimeInterval kAnimataionDuataion = 0.5;@implementation EOCAnimatedView@end

常量要同时用staticconst来声明。如果试图修改由const声明的变量,那么编译器就会报错。

而static修饰符则意味着该变量仅在定义此变量的编译单元(在OC环境下,“编译单元”通常指某个类的实现文件)中可见。编译器没收到一个编译单元,就会输出一份“目标文件”,因此kAnimationDuration变量,其作用域仅限于由EOCAnimatedVIew所生成的目标文件。假如声明此变量不加static,编译器会为它创建一个“外部符号”。此时若另一个编译单元也声明了同名变量,那么编译器会抛错误:

duplicate symbol _kAnimationDuration in: EOCAnimatedView.o EOCOtherView.o
如果一个变量既声明为static,也声明为const,那么编译器根本不会创建符号,而是像#define预处理指令一样,把所有kAnimationDuration 都替换为常值。

3 公开常量

如果打算公开某个常量。如:在类代码中调用NSNotificationCenter通知其他类,用一个对象来派发通知,。派发通知时,需要使用字符串来表示此项通知的名称,这个名字就可以声明为一个外界可见的常值变量。这样,注册者无须知道实际字符串值,只需以常值变量来注册资金想要接收的通知即可。

定义:

//In the header file extern NSString *const EOCStringConstant;//In the implementation fileNSString *const EOCStringConstant = @"VALUE";
注意const修饰符在常量类型中的位置。常量定义应从右至左解读。EOCStringConstant就是“一个常量,而这个常量是指针,指向NSString对象”。

编译器看到头文件中的extern关键字,就能明白如何在引入头文件的代码中处理该常量了。这个关键字是要告诉编译器,在全局符符号表中将会有一个名叫EOCStringConstant的符号。也就是会所,编译器无须查看其定义,即允许代码使用此常量。因为它知道,当链接成二进制文件后,肯定能找到这个常量。

因为符号要放在全局符号表中,所以命名时需谨慎。为避免名称冲突,最好用与之相关的类名做前缀。系统框架一般都这样做。如:UIKit中,

UIApplicationDidEnterBackgroundNotification 

UIApplicationWillEnterForegroundNotification。 


4 要点

1 不要用预处理指令定义常量。这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换操作。即使有人重现定义了常量值,编译器也不会产生警告⚠️,这将导致应用程序中的常量值不一致。

2 在实现文件中使用static const来定义“只在编译单元内可见的常量”。由于此类常量不在全局符号表中,所以无须为其名称加前缀。

3 在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以其名称应加以区隔,通常用与之相关的类名做前缀。


0 0
原创粉丝点击