为什么不要工程中不要随意使用define定义常量
2017-06-06 12:18
267 查看
首先,预处理命令他不是一个常量!!!!
我们来看一段代码
这段代码会输出多少,我们将“avatar”定义为了60,然后在一个永远不会执行的代码里面重新定义了“avatar”为80,if语句中的代码永远不会执行,但是在编译时期,编译器会编译这段代码,而这个时候编译器就会将avatar这个名字替换为@“80”,所以这段代码最后的输出结果就是80。
当然这个时候编译器是会有一个警告的,但是不知道有多少同学会忽略这个警告。或者你会告诉我你对警告十分敏感,不会放过他的,但是记住你不是一个人在写代码,可能在别人的页面他给你重新定义了你的define,给你挖了一个大坑,还找不着.........
用const来定义一个常量
const修饰符定义的变量是不可变的,比如说你需要定义一个动画时间的常量,你可以这么做:
当你试图去修改“ kAnimateDuration”的值的时候,编译器会报错。更加重要的是用这种方法定义的常量是带有类型信息的,而这点则是define不具备的。
也许你已经发现了,如果你像这样定义:
你是可以修改userName的值的,(说好的常量呢~~~)
首先我们需要确定的是以下两种写法是一样的:
也就是说const放在类型前还是类型后是一样的效果。然后不同效果的是下面这种写法:
const 修饰的是他右边的部分,也就是说:
当const修饰的是(userName)的时候,不可变的是userName;“*”在C语言中表示
指针指向符,也就是说这个时候userName指向的内存块地址不可变,而内存保存的内容是可变的,我们来做个尝试:
以上NSLog会打印*userName指向的内存块地址,而他的输出是:
![](http://files.jb51.net/file_images/article/201608/201683180514290.jpg?20167318526)
输出
我们已经发现当我们改变内存的内存的时候他的地址并没有发生改变,也就是说这是符合“const”修饰符的规定的。
而当我们的修饰符是这样的时候:
我们则无法改变userName的值。
所以当我们需要定义一个不可变的常量的时候 ,我们还是需要将“const”修饰符放到“*”指针指向符后边才对。
一定要同时使用static和const来定义你的变量
上面已经说了const是用来定义一个常量。而static在C语言中(OC中延用)则表明此变量只在改变量的输出文件中可用(.m文件),如果你不加“static”符号,那么编译器就会对该变量创建一个“外部符号”,后果是什么呢?
你可以尝试在不同编译文件中加入以下代码:
可能尽管文件之间并没有相互引用,不存在属性名重复的问题(因为这并不是一个属性,这是一个外部符号),但是编译器还是报错了:
![](http://files.jb51.net/file_images/article/201608/201683180646876.jpg?20167318655)
他会告诉你在两个目标文件(.0文件是.m文件编译后的输出文件)有一个重复的符号。(OC中没有类似C++中的名字空间的概念)
所以当你在你自己的.m文件中需要声明一个只有你自己可见的局部变量(k开头)的变量的时候一定要同时使用“static”和“const”两个符号。
定义工程中的全局变量
在我们的工程中一定会定义很多全局常量,很多人的做法是会创建一个“ constant.h”文件,在这个文件中用#define声明许多常量,然后将这个头文件引入“pch”文件中,不能说这么做不对,但是如同上面说的那样define可能被修改,当然在命名规范的情况下这种情况很少出现,并且这样做的效率很高。
然而苹果更推荐另外一种做法:"extern",这样做的优势是保持常量绝对不会被修改,并且一定初始化还带有类型信息。
我们在"constants.h"文件中,声明常量:
然后在“constants.m”中定义他:
用“extern”定义的常量必须也只能初始化一次,不满足必须以及只能一次的条件那么编译器就会提醒你。在定义全局变量的时候需要要注意你的命名,你可以使用规定好的前缀来命名。
“define”和“extern”各有各的优势,不过我个人还是比较推荐使用“extern”.(因为之前在一个工程中被define坑惨了!)。
我们来看一段代码
#define avatar @"60" if (false) { #define avatar @"80" } NSLog(avatar);
这段代码会输出多少,我们将“avatar”定义为了60,然后在一个永远不会执行的代码里面重新定义了“avatar”为80,if语句中的代码永远不会执行,但是在编译时期,编译器会编译这段代码,而这个时候编译器就会将avatar这个名字替换为@“80”,所以这段代码最后的输出结果就是80。
当然这个时候编译器是会有一个警告的,但是不知道有多少同学会忽略这个警告。或者你会告诉我你对警告十分敏感,不会放过他的,但是记住你不是一个人在写代码,可能在别人的页面他给你重新定义了你的define,给你挖了一个大坑,还找不着.........
用const来定义一个常量
const修饰符定义的变量是不可变的,比如说你需要定义一个动画时间的常量,你可以这么做:
static const NSTimeInterval kAnimateDuration = 0.3;
当你试图去修改“ kAnimateDuration”的值的时候,编译器会报错。更加重要的是用这种方法定义的常量是带有类型信息的,而这点则是define不具备的。
也许你已经发现了,如果你像这样定义:
static const NSString * kUserName = @"StrongX";
你是可以修改userName的值的,(说好的常量呢~~~)
首先我们需要确定的是以下两种写法是一样的:
static NSString const * kUserName = @"StrongX"; static const NSString * kUserName = @"StrongX";
也就是说const放在类型前还是类型后是一样的效果。然后不同效果的是下面这种写法:
static NSString * const kUserName = @"StrongX";
const 修饰的是他右边的部分,也就是说:
static NSString const * kUserName = static NSString const (* kUserName ) static NSString * const kUserName = static NSString * const (kUserName)
当const修饰的是(userName)的时候,不可变的是userName;“*”在C语言中表示
指针指向符,也就是说这个时候userName指向的内存块地址不可变,而内存保存的内容是可变的,我们来做个尝试:
NSLog(@"内存地址: %x",& kUserName); kUserName = @"superXLX"; NSLog(@"内存地址: %x",& kUserName);
以上NSLog会打印*userName指向的内存块地址,而他的输出是:
![](http://files.jb51.net/file_images/article/201608/201683180514290.jpg?20167318526)
输出
我们已经发现当我们改变内存的内存的时候他的地址并没有发生改变,也就是说这是符合“const”修饰符的规定的。
而当我们的修饰符是这样的时候:
static NSString * const kUserName = @"StrongX";
我们则无法改变userName的值。
所以当我们需要定义一个不可变的常量的时候 ,我们还是需要将“const”修饰符放到“*”指针指向符后边才对。
一定要同时使用static和const来定义你的变量
上面已经说了const是用来定义一个常量。而static在C语言中(OC中延用)则表明此变量只在改变量的输出文件中可用(.m文件),如果你不加“static”符号,那么编译器就会对该变量创建一个“外部符号”,后果是什么呢?
你可以尝试在不同编译文件中加入以下代码:
NSString * const kUserName = @"StrongX";
可能尽管文件之间并没有相互引用,不存在属性名重复的问题(因为这并不是一个属性,这是一个外部符号),但是编译器还是报错了:
![](http://files.jb51.net/file_images/article/201608/201683180646876.jpg?20167318655)
他会告诉你在两个目标文件(.0文件是.m文件编译后的输出文件)有一个重复的符号。(OC中没有类似C++中的名字空间的概念)
所以当你在你自己的.m文件中需要声明一个只有你自己可见的局部变量(k开头)的变量的时候一定要同时使用“static”和“const”两个符号。
定义工程中的全局变量
在我们的工程中一定会定义很多全局常量,很多人的做法是会创建一个“ constant.h”文件,在这个文件中用#define声明许多常量,然后将这个头文件引入“pch”文件中,不能说这么做不对,但是如同上面说的那样define可能被修改,当然在命名规范的情况下这种情况很少出现,并且这样做的效率很高。
然而苹果更推荐另外一种做法:"extern",这样做的优势是保持常量绝对不会被修改,并且一定初始化还带有类型信息。
我们在"constants.h"文件中,声明常量:
extern NSString *const XUserName;
然后在“constants.m”中定义他:
NSString *const XUserName = @"StrongX";
用“extern”定义的常量必须也只能初始化一次,不满足必须以及只能一次的条件那么编译器就会提醒你。在定义全局变量的时候需要要注意你的命名,你可以使用规定好的前缀来命名。
“define”和“extern”各有各的优势,不过我个人还是比较推荐使用“extern”.(因为之前在一个工程中被define坑惨了!)。
相关文章推荐
- 在PHP中,使用函数define()来定义常量 可设置大小号是否敏感等
- FOUNDATION_EXPORT的使用(定义常量和define类似)
- PHP常量声明、使用方法|define()函数定义常量类似于全局
- php问题一:为什么在入口文件index.php中定义的常量,在concroller中也能使用?
- 为什么 接口只用于定义类型,不应该使用常量接口 ?
- 常量的定义和使用
- define宏定义和const常量定义之间的区别
- 为什么在定义符号常量时const pi=3.14 没出错 而且和const int pi=3.14时 输出pi的值是一样的 但不等于3.14
- 我的SQL Server函数为什么结果不对(使用CHAR/VARCHAR要定义长度,不然默认长度为1)
- 为什么要在定义抽象类时使用abstract关键字
- 看到这个你能明白很多为什么要定义new,为什么函数不要定义
- DLL中定义的类可以在应用工程中使用
- 定义常量时const和define的区别
- 通过类字面常量解释接口常量为什么只能定义为static final,类加载过程---Thinking in java
- 为什么在中断向量表中不直接LDR PC ,"异常地址"。而是使用一个标号,然有再在后面使用 DCD定义这个标号
- define宏定义和const常量定义之间的区别
- PHP中常量的定义和使用
- 为什么要在定义抽象类时使用abstract关键字
- 使用SharePoint Solution Generator建立SharePoint的List定义工程
- 为什么要在定义抽象类时使用abstract关键字