本节书摘来自华章出版社《编写高质量代码:改善Objective-C程序的61个建议》一 书中的第1章,第1.3节,作者:刘一道,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
建议3:尽量使用const、enum来替换预处理#define
#define
定义了一个宏,在编译开始之前就会被替换。const只是对变量进行修饰,当试图去修改该变量时,编译器会报错。在一些场合里你只能用 #define,而不能用 const。理论上来说,const 不仅在运行时需要占用空间,而且还需要一个内存的引用;但从时间上来说,这是无关紧要的,编译器可能会对其进行优化。
const在编译和调试的时候比 #define 更友好。在大多数情况下,当你决定用哪一个时,这是你应该考虑的一个非常重要的点。
想象一下,下面这样一个应该使用 #define 而不是 const 的场景:如果想在大量的 .c 文件中使用一个常量,只需要使用 #define 放在头文件中;而使用 const,则需要在 .c 文件和头文件中都进行定义,如下所示。
// in a C file
const int MY_INT_CONST = 12345;
// in a header
extern const int MY_INT_CONST;
MY_INT_CONST ,在任何 C 文件中都不能当作一个静态变量或全局作用域使用,除非它已被定义。然而,对于一个整型常量,你可以使用枚举(enum)。事实上,这就是 Apple一直在做的事情。它(enum)兼有 #define 和 const 的所有优点,但是只能用在整型常量上。
// In a header
enum
{
MY_INT_CONST = 12345,
};
哪一个更高效或更安全呢?#define 在理论上来说更高效,但就像之前说的那样,在现代的编译器上,它们可能没什么不同。#define 会更安全,因为当试图赋值给它时,总会出现一个编译器错误。
因此,相对字符串字面量或数字,更推荐适用常量。应使用static方式声明常量,而非使用#define的方式来定义宏。
恰当用法如下所示:
static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company";
static const CGFloat NYTImageThumbnailHeight = 50.0;
不当用法如下所示:
#define CompanyName @"The New York Times Company"
#define thumbnailHeight 2
对于整型类型,代替#define比较好的方法是使用enum,在使用enum时,推荐使用最新的fixed underlying type规范的NS_ENUM和NS_OPTIONS宏,因为它们是基于C语言的枚举,保留了C语言的简洁和简单的特色。这些宏能明确指定枚举类型、大小和选项,改善在Xcode中的代码质量。此外,在旧的编译器中,这种语法声明能正确编译通过,在新的编译器中也可明解基础类型的类型信息。
下面,使用NS_ENUM宏定义枚举。该组值是互斥的,代码如下:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
在本例中,在命名UITableViewCellStyle的NSInteger类型时,通过使用NS_ENUM宏使界定双方的名称和枚举类型更容易了。
在下面的代码中,通过使用NS_OPTIONS宏定义了一组可以组合在一起的位掩码值,实现方式如下:
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
像枚举一样,NS_OPTIONS宏定义了一个名称和一个类型。然而,对于选项的类型通常应该是NSUInteger 。
在实际编码中,如何使用枚举宏来更换enum,比如这样一个enum定义:
enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;
用NS_ENUM宏来实现上面的定义,其语法如下:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
在实际开发中,经常会使用enum来定义一个位掩码,如下面一个用enum来定义位掩码的示例:
enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;
对于上面的通过用enum定义位掩码的示例,可使用NS_OPTIONS宏实现如下:
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
或者,也可以使用现代化的Objective-C的转换器在Xcode自动进行此更改。