本文要点:
- 多用类型常量,少用#define预处理指令
- 用枚举表示状态、选项、状态码
多用类型常量,少用#define预处理指令
使用#define的弊端
场景:编写代码时候我们经常要定义常量,例如我们在编写一段动画的逻辑,我们把一个动画的时常提取为常量:
#define ANIMAITON_DURATION 0.3
上面的编写姿势是不规范的,Why? 如下:
#define
预处理指令定义常量有两个弊端:
-
#define
定义的常量没有类型信息 上述代码我们采用#define预处理指令,会把源码中ANIMAITON_DURATION字符串全部替换为 0.3,不过我们却无法获知 0.3的类型信息,这会使我们开发中产生迷惑 -
#define
定义的常量可能会在无意中被人修改了,如果有人重新定义了常量,原有的值会被覆盖
#define ANIMAITON_DURATION 0.3
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 重新定义了 ANIMAITON_DURATION
#define ANIMAITON_DURATION 0.4
float value = 1.0 + ANIMAITON_DURATION;
NSLog(@"%f",value);
}
如上输出结果是 1.4,原有的值0.3被覆盖了
定义常量正确的姿势
定义常量的正确的姿势: 使用类型常量
static const NSTimeInterval KAnimationDuration 0.3
上述定义的类型常量包含了类型信息,清楚的描述了常量的定义
具体使用:
- 私有常量
如果不打算公开某个常量,比如该变量的使用,应把常量定义在常量的编译单元(tanslation unit,也就是.m文件)内,并建议在前面加上k字母;
变量同时用 static 和const 修饰,const表示该变量的值不可修改,static修饰表示该变量仅在定义该变量的编译单元内可见,
例子:MBProgressHUD
MBProgressHUD.m
定义了关于间距,字体大小的私有常量
static const CGFloat kPadding = 4.f;
static const CGFloat kLabelFontSize = 16.f;
static const CGFloat kDetailsLabelFontSize = 12.f;
- 公开常量
有时候我们需要对外公开某个常量,比方说,你可能在类代码中调用了NSNotificationCenter派发通知,派发通知的时候,需要使用字符串常量标识通知的名称,而这个名称需要声明为一个外界可见的常量变量,这样注册者可以方便的调用;
公开常量在头文件中用extern声明,并在相关实现文件中定义其值;
公开常量放在全局符号表中(global symbol table),命名时候通常以与之相关的类名作为前缀,以避免名称冲突
例子: SVProgressHUD
SVProgressHUD.m
文件中定义了与SVProgressHUD状态相关的几个通知
NSString * const SVProgressHUDDidReceiveTouchEventNotification = @"SVProgressHUDDidReceiveTouchEventNotification";
NSString * const SVProgressHUDDidTouchDownInsideNotification = @"SVProgressHUDDidTouchDownInsideNotification";
NSString * const SVProgressHUDWillDisappearNotification = @"SVProgressHUDWillDisappearNotification";
NSString * const SVProgressHUDDidDisappearNotification = @"SVProgressHUDDidDisappearNotification"
SVProgressHUD.h
文件对外公开了这些常量
extern NSString * const SVProgressHUDDidReceiveTouchEventNotification;
extern NSString * const SVProgressHUDDidTouchDownInsideNotification;
extern NSString * const SVProgressHUDWillDisappearNotification;
extern NSString * const SVProgressHUDDidDisappearNotification;
extern NSString * const SVProgressHUDWillAppearNotification;
extern NSString * const SVProgressHUDDidAppearNotification;
注意const修饰的字符在常量类型的位置,常量定义应该从右往左边读,所以上述SVProgressHUDDidReceiveTouchEventNotification 是一个常量,而这个常量是一个指针,指向NSString对象,我们需要用const锁定这个指针
用枚举表示状态、选项、状态码
C语言枚举语法回顾
enum EOCConnectionState {
EOCConnectionStateDisconnected,
EOCConnectionStateConnecting,
EOCConnectionStateDisconnected,
}
定义枚举变量的方式:
enum EOCConnectionState state = EOCConnectionStateDisconnected;
上述的语法不太简洁,我们可以使用typedef重新定义枚举类型
typedef enum EOCConnectionState = EOCConnectionState
// 使用EOCConnectionState代替enum EOCConnectionState
EOCConnectionState state = EOCConnectionStateDisconnected
我们还可以指定枚举使用何种底层数据类型如下,并且手工指定枚举成员的值
enum EOCConnectionState:NSUInteger {
EOCConnectionStateDisconnected = 1,
EOCConnectionStateConnecting,
EOCConnectionStateDisconnected,
}
如上指定了枚举的底层数据类型是NSUInteger,而且手动指定了枚举成员初始值为1
如果枚举成员的值可以组合,我们一般用位移来表示,如下
enum EOCConnectionState:NSUInteger {
EOCPerimttedDirectionUP = 1<<0,
EOCPerimttedDirectionDown = 1<<1,
EOCPerimttedDirectionLeft = 1<<2,
EOCPerimttedDirectionRight= 1<<3,
}
1 << 0 二进制 : 00000001 十进制 1
1 << 1 二进制 : 00000010 十进制 2
1 << 2 二进制 : 00001000 十进制 4
1 << 3 二进制 : 00010000 十进制 8
使用位移指定枚举的值得好处是我们可以方便使用位操作符 | 和 &操作 枚举成员,
|操作:如果我们要组合两个枚举成员,我们可以用 |操作:
enum EOCConnectionState statues = EOCConnectionStateDisconnected | EOCConnectionStateConnecting
上述代码的执行过程:
二进制 十进制
00000001 1 EOCConnectionStateDisconnected
00000010 2 EOCConnectionStateConnecting
00000011 3 EOCConnect ionStateDisconnected|EOCConnectionStateConnecting
&操作:使用 &位操作符判断是否启用某个选项:
if(status & EOCConnectionStateDisconnected) {
//EOCConnectionStateDisconnected is set
}
Objcet-C中枚举
typedof NS_NUM (NSUInteger,EOCConnectionState) {
EOCConnectionStateDisconnected ,
EOCConnectionStateConnecting,
EOCConnectionStateDisconnected,
};
NS_NUM( _type, _name)是一个辅助宏,上面的语法展开如下,实际上就是C语言的枚举定义方式:
typedf enum EOCConnectionState:NSUInteger EOCConnectionState;
enum EOCConnectionState:NSUInteger {
EOCConnectionStateDisconnected ,
EOCConnectionStateConnecting,
EOCConnectionStateDisconnected,
};
这里要注意,当需要枚举成员组合成新值得时候,也就是按位操作枚举成员的时候,我们一般使用NS_OPTIONS来定义枚举成员,与使用NS_NUM相比,NS_OPTIONS会帮我们在进行枚举成员的位运算过程中做隐式转换,保证枚举成员进行位运算的正确性
typedof NS_OPTIONS (NSUInteger,EOCPerimtted) {
EOCPerimttedDirectionUP = 1<<0,
EOCPerimttedDirectionDown = 1<<1,
EOCPerimttedDirectionLeft = 1<<2,
EOCPerimttedDirectionRight= 1<<3,
};
使用场景:
在表示一系列逻辑相似的状态,选项,我们应考虑使用枚举的方式,
我们可以在SVProgressHUD
这个项目的SVProgressHUD.h
中找到如下的代码
typedef NS_ENUM(NSUInteger, SVProgressHUDMaskType) {
SVProgressHUDMaskTypeNone = 1, // allow user interactions while HUD is displayed
SVProgressHUDMaskTypeClear, // don't allow user interactions
SVProgressHUDMaskTypeBlack, // don't allow user interactions and dim the UI in the back of the HUD
SVProgressHUDMaskTypeGradient // don't allow user interactions and dim the UI with a a-la-alert-view background gradient
};
上面分别代表了SVProgressHUD几种显示样式
使用建议:
我们使用switch语法会习惯加上default分支,但在使用switch处理枚举类型数据相关逻辑时候,建议去掉default分值,这样做的好处是假如某个枚举值没有处理,编译器就会发出警告,提醒我们对所有的枚举选项都进行处理
typedef NS_ENUM (NSUInteger, CWGConnectionState) {
CWGConnectionStateDisconnected,
CWGConnectionStateConnecting,
CWGConnectionStateConnected,
};
switch (_currentState) {
CWGConnectionStateDisconnected:
// Handle connecting state
break;
CWGConnectionStateConnecting:
// Handle disconnected state
break;
CWGConnectionStateConnected:
// Handle connected state
break;
};
网友评论