关于 const、enum 和 #define

关键词:const、enum、#define、static、枚举

文章是参考书籍与博客的总结,自己写下来也算是自我总结,加深印象~

一、前言

"#define" 定义了一个宏,在编译开始之前就会被替换;理论上说,#define 更高效、更安全;高效在现在的硬件来说,几乎可以忽略;而安全性体现在,对一个 #define 的值进行赋值时,编译器会报错;缺陷也很明显,由于重复定义不会警告或报错,举个例子,如果在一个头文件中定义了一个 #define SOMEVALUE,其他引用这个头文件的类中所有用到 SOMEVALUE 的都会被替换,可能其他文件中也定义了这个 #define,这就产生不一致的值,影响程序;

const 只是对变量进行修饰,修改会报错;理论上说,const 运行时占用空间,还需要一个内存的引用;但从时间上来说,这是无关紧要的;

在某些场景下应该使用 #define 而不是 const,例如想在大量的 .c 文件中使用一个常量,只需要使用 #define 放在头文件中;而使用 const,则需要在 .c 文件和头文件中都进行定义。

而对于整形常量,我们就可以使用 enum 枚举;

二、const static

变量一定要同时用 static 与 const 来声明。如果试图修改由 const 修饰符所声明的变量,那么编译器会报错。static 修饰符意味着该变量仅在定义此变量的便一单元中可见。假如声明此变量时不加 static, 则编译器会为它创建一个“外部符号”(external symbol)。此时,若是另一个编译单元中也声明了同名变量,那么编译器会抛出一条错误信息,如下图,ClassA 和 ViewController 中都声明了kAnimationDuring 变量,没有加 const,那么编译会报错:

Paste_Image.png
Paste_Image.png
Paste_Image.png

实际上,如果一个变量既声明为 static,又声明为 const,那么编译器根本不会创建符号,而是会像 #define 预处理指令一样,把所有遇到的变量都替换为常值,只不过这种方式是带有类型信息的。

有时候需要对外公开某个常量,比如派发通知时。此常量需放在“全局符号表”(global symbol table) 中,以便可以在定义该常量的编译单元之外使用。因此,其定义方式上与 static const 有所不同。而是应该这样的:

//在 .h 头文件中
extern NSString *const AClassConst;
//在 .m 实现文件中
NSString *const AClassConst = @"value";

即在头文件中声明,并在实现文件中定义。编译器看到头文件中的 extern 关键字,就明白如何在引入此头文件的代码中处理常量。因为编译器知道,在全局符号表中有一个叫 AClassConst 的符号。编译器无需查看其定义,链接成二进制文件后,一定能找到这个常量。
此类常量必须要定义,且只能定义一次。通常定义在实现文件中,如果不定义的话,编译器在使用到这个值的时候会报错。为避免命名冲突,前缀加上类名。

注意 const 修饰符在常量类型中的位置。常量定义从右至左解读,如上例 AClassConst 就是一个 “一个常量,而这个常量是指针,指向 NSString 对象”。这与需求相符合:我们不希望有人改变此指针常量,使其指向另一个 NSString 对象。下面举例说明:

先说一下非指针类型的顺序的用法

static const CGFloat XXXHeight = 50.f;
static CGFloat const XXXHeight = 50.f;
这两种写法一样,修改值编译器都会报错
extern CGFloat const XXXHeight;
extern const CGFloat XXXHeight;
同样修改值的话,编译器会报错
再举个例子说一下 NSString 类型的用法
extern NSString *const AClassConst,正如之前所讲的,这种方式修改值的话会报错;
extern NSString const *AClassConst,而这么写的话,值可以被正常修改;
static const CGFloat XXXWidth = 40.f,不可被修改;
static CGFloat const XXXWidth = 40.f,不可被修改;

规范一下用法

staitc NSString *const XXXName = @"name";
static const CGFloat XXXWidth = 50.f;
extern NSString *const YYYName;
NSString *const YYYName = @"VALUE";
extern CGFloat const YYYHeight;
CGFloat const YYYHeight = 30.f;

关于 static、const、extern 等详细信息,自行网上查阅

三、多用枚举表示状态、选项等

枚举平时用到的非常多,注意使用最新规范的 NS_ENUM 和 NS_OPTIONS 宏。

如下,枚举的值是互斥的
typedef NS_ENUM(NSInteger, UITableViewRowAnimation) {
    UITableViewRowAnimationFade,
    UITableViewRowAnimationRight,           // slide in from right (or out to right)
    UITableViewRowAnimationLeft,
    UITableViewRowAnimationTop,
    UITableViewRowAnimationBottom,
    UITableViewRowAnimationNone,            // available in iOS 3.0
    UITableViewRowAnimationMiddle,          // available in iOS 3.2.  attempts to keep cell centered in the space it will/did occupy
    UITableViewRowAnimationAutomatic = 100  // available in iOS 5.0.  chooses an appropriate animation style for you
};
也可以指定从某个值开始
/**银行卡功能类型*/
typedef NS_ENUM(NSInteger, CardFundingType) {
    CardFundingTypeCredit = 1,    //信用卡
    CardFundingTypeDebit,         //借记卡
    CardFundingTypeBank,          //银行卡 (包含信用卡和借记卡)
    CardFundingTypeOther,
};

枚举相信大家使用的也非常多,常规的自不必说,这里着重说一下 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
};
self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin| UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
看到这里可能大家就回想起来了

再例如,我们有个功能要分享到第三方应用,可以是微信、QQ、微博、朋友圈,QQ空间等等,而分享到哪里是可以由后端配置的,这时我们可以使用 NS_OPTIONS 来实现。将各个选项值定义为 2 的幂即可。

关于枚举的用法,要注意在 switch 语句使用时注意。我们总是习惯在 switch 语句中加上 default 分支。然而,若是用枚举来定义状态机,则最好不要有 default 分支。这样的话,如果稍后又加了一种状态,那么编译器就会发出警告信息,提示加入新的状态并在 switch 分支中处理。

typedef NS_ENUM(NSUInteger, KUIStyle) {
    KUIStyle0                 = 0,
    KUIStyle1                 = 1,
    KUIStyle2                 = 2,
    KUIStyle3                 = 3,
};
@property (nonatomic, assign) KUIStyle uiStyle;
如下图所示,如果注释掉 default,switch 分支枚举不全的时候编译器会报错,可以避免漏掉新加的类型。
1.png
四、总结

1.尽量避免使用 #define 预处理命令,它不包含任何类型信息,仅仅是在编译前替换。最终要的是重复定义并不会发出警告,容易在程序中产生不一致的值。

2.在源文件 .m 中定义的 static const 类型常量因为无需全局引用,所以命名不需要包含命名空间,在前面加个小写字母 k 即可。而在 .h 中定义的全局引用常量,需要关联定义在 .m 中的部分。因为全局,所以需要包含命名空间,通常类名最为前缀即可。

3.尽量使用 NS_ENUM 和 NS_OPTIONS 宏来实现枚举。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • static 修饰局部变量 让局部变量只初始化一次 局部变量在程序中只有一份内存 并不会改变局部变量的作用域,仅仅...
    大冲哥阅读 1,340评论 0 1
  • (1)可以定义 const 常量 (2)const 可以修饰函数的参数、返回值. 详细内容: 1、什么是const...
    幽鬼09阅读 705评论 0 4
  • 常量 程序中的某些信息的值是永远不会变的,这类不变的值称为常量(constant)。 在 Objective-C ...
    独木舟的木阅读 572评论 0 0
  • 刚从事IOS这行的时候,const很少用,主要是用#define和enum,后来慢慢的注意到,如何提升代码的...
    张付东阅读 2,355评论 0 3
  • 借了春风十里 沁润一身新碧 去了枝干的心焦 款款而绿丝绦 柔韧了蛮腰的枝条 向湖心深处曼妙 积了一冬的恋情 只等 ...
    微雨凭栏阅读 270评论 2 3