惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
博客园 - 司徒正美
博客园 - 【当耐特】
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
IT之家
IT之家
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
宝玉的分享
宝玉的分享
V
V2EX
S
SegmentFault 最新的问题
V
Visual Studio Blog
阮一峰的网络日志
阮一峰的网络日志
Martin Fowler
Martin Fowler
Jina AI
Jina AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园_首页
L
LangChain Blog
D
Docker
腾讯CDC

土土哥的Blog

好久没有更新博客=。= 阿里巴巴国际无线技术部 - 招人啦~求iOS、Android、Java 转-阿里巴巴国际无线技术部 - 在这里遇见最好的自己 反编译分析并模拟实现methodSignatureForSelector方法 反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash 有趣的Autolayout示例5-Masonry实现 在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper 用QuartzCode快速实现一个收藏动画 开源项目-拼图验证控件TTGPuzzleVerify的实现 Swift开源Mac App - BingWallPaper 有趣的Autolayout示例4-Masonry实现 翻译-为什么objc_msgSend必须用汇编实现 API返回结果设计经验与总结 结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing 总结一些iOS项目中组织代码的方法 对组件化与模块化的思考与总结 开源项目-TTGTagCollectionView 有趣的Autolayout示例3-Masonry实现 Swift开源项目: TTGEmojiRate的实现 Swift写的库-TTGSnackbar 有趣的Autolayout示例2-Masonry实现 解决iOS项目的版本兼容问题-结合宏、Category和Runtime 用Runtime的手段填充任意NSObject对象的nil属性 有趣的Autolayout示例-Masonry实现 UITextView编辑时插入自定义表情-续-自定义表情图片的大小 RPC框架Thrift例子-PHP调用C++后端程序 GCD使用经验与技巧浅谈 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权 Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5 @autoreleasepool-内存的分配与释放
Effective-Objective-C-读书笔记-Item-4-如何正确定义常量
土土哥 · 2015-03-11 · via 土土哥的Blog

前言

写博客果然比光看书要花时间=。=,书都快看完了,才写到第4节。但是总结一遍,的确印象深刻啊~~

这次的主题是“常量”,非常常用,又非常重要的东西~良好的代码规范、设计,少不了各种常量

Item 4 - Prefer Typed Constants to Preprocessor #define

本节着重对比了常量的定义形式,类型常量和#define的区别、各自的特点。最后得出,我们应该用带有类型的常量,而不是#define。

一个简单的示例

应用中,少不了各种动画,也就少不了设定动画的时间长度,一般来说,我们会设定短、中、长几种时间常量,用于不同的场合,了解C语言的童鞋估计对“#define”宏定义不陌生,于是,可能就会如下这么定义:

1
2
3
#define ANIMATION_DURATION_SHORT 0.3
#define ANIMATION_DURATION_MIDDLE 0.6
#define ANIMATION_DURATION_LONG 1.0

于是,所有用到动画时长的地方,直接用这几个宏定义就行了。

乍一看,似乎没什么问题。

类型+不可变

先来看看宏定义干了什么。
总的来说,#define就是“替换”,在编译时,将一切宏定义替换成定义后面的内容。(参考百度百科

关键在于:

  • 宏定义不会检查替换的“类型”,只要遇到,就做替换。
  • 宏定义不保证“不可变”,因为可以重复定义,不能保证值不变。(这个我觉得是最容易让人忽略的地方)

所以,用宏定义定义常量,很“危险”。

正确的做法

恰当的常量,应该具备合适的常量名字、正确的类型,还有const、extern等关键字的修饰,当然,还要根据常量适用的“范围”,做出恰当的处理。一下举例说明。

只在函数中用的常量

只在函数中用的常量:

1
2
3
4
5
- (void)runAnimation {

static const NSTimeInterval kAnimationDurationLong = 1.0f;

}

static、const定义了它是静态不可变的;NSTimeInterval则“精确”指定了常量的类型。

只在一个“.m”文件中使用的常量

在类的实现文件“xxx.m”文件中定义常量,也是很常见的。

你可能会觉得,既然是一个类使用的,那就放在类的头文件里面定义不就好了。

但是这么想是错的=。=

因为所有要用到这个类的文件,都会无形当中包含了这些常量声明,将不必要的数据暴露在外面,这样是违反设计原则的,并且,如果常量名字取得不恰当,还有可能造成冲突。

所以,应该如下:

1
2
3
4
5
6
7
8



static const NSTimeInterval kAnimationDurationLong = 1.0f;

@implementation XXX

@end

需要注意的点:

  • 一定要放在.m文件中。
  • 必须是static,表示此常量作用域只在此.m文件中。如果不加static的话,常量就会在全局作用域中声明,可能会导致跟其他的常量冲突。
  • const,当然。
  • NSTimeInterval,确定的类型。
  • kAnimationDurationLong,“k”开头的命名,也算是Apple的规范了吧,内部常量统一以“k”开头。

全局常量

一般来说,最常见的全局常量就是发广播Notification时用的Notification的名字,往往都是一些字符串,下面就以这个为例子:

假设我们的类叫“TTGClass”,我们的类会向外发送Notification,所以需要定义一个Notification的名字常量供外界注册,如下:

1
2
3
4
5

extern NSString * const TTGClassWorkBeginNotification;


NSString * const TTGClassWorkBeginNotification = @"TTGClassWorkBeginNotification";

关键:

  • 头文件中只是做声明,不做定义。所以用extern表示常量在别处定义的,尽量隐藏细节。
  • 类名做常量名字的开头,这个非常关键,因为Objective-C里面没有像Java那样的“包”机制,所以必须靠命名来划分不同的类,常量当然也要遵循这个规则。
  • NSString,确定的类型;const,表明常量;Notification后缀,表明用途。这些都是需要注意的。

总结

良好的代码,总是少不了定义各种常量,以避免“魔鬼数字、魔鬼字符串”。所以我们应该尽量避免少用#define定义常量,而是用带有类型的、const的常量,并且尽可能的缩小常量的作用域。

暴露的细节越少,出错的机会也就越小(忘了从哪里看到的=。=)