《编写高质量代码:改善Objective-C程序的61个建议》——建议3:尽量使用const、enum来替换预处理#define

简介:

本节书摘来自华章出版社《编写高质量代码:改善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自动进行此更改。

f6de38e328bc818f8ef5d8da1a0fd3fb17e5a56c
相关文章
|
程序员 iOS开发
《编写高质量代码:改善Objective-C程序的61个建议》——导读
我一直在思考,如何才能编写出高质量、优秀的代码,我也在不停地探寻,希望找出类似于武侠小说中所说的武功秘籍,在编写代码一途可以帮助大家走“捷径”从而达到事半功倍的效果。
1041 0