iOS 中#define和const的使用细节

在编码过程中,我们需要使用定义一些局部或者全局访问的数据类型。比如下面的两种方式都可以定义了一个动画播放时长的常量:

  • #define ANIMATION_DURATION 3
  • static const NSTimeInterval AnimationDurtion = 3

使用define和const都可以达到目的,但是他们到底有什么不同,使用的时候要注意什么?

下面我们就来一探究竟。


define和const的区别

  • 宏:只是在预处理器里进行文本替换,没有类型,不做任何类型检查,编译器可以对相同的字符串进行优化。对同一个宏在多个地方引用,每个引用都需要开辟一块独立的内存用来保存宏,大量用宏会导致二进制文件变大。
  • const:共享一块内存空间,就算项目中N处用到,也不会分配N块内存空间,可以根据const修饰的位置设定能否修改,在编译阶段会执行类型检查。

我们先记住一点:const右边为定义的常量,无法修改


如何定义常量

在使用const的时候,我们发现const的位置可以有很多种,如下所示,那么他们到底有什么区别呢?我们在定义const的时候到底该如何选择呢?

    const NSString *string1 = @"111111";//情形1
    NSString const *string2 = @"222222";//情形2
    NSString *const string3 = @"333333";//情形3

编译运行上面的代码提示如下:

分析:

1、情形1和情形2实际上是一样的。分析下为什么情形1和情形2的时候*string1 = replaceString会报错呢?

因为*string 1位于const右边,也就是此时*string是常量,是无法被修改的,报错已经很明确的显示出来了。readonly的变量不能被修改。但是可以修改string1,因为string1不是常量,*string1才是常量。这不是我们想要的结果。

2、情形3的两种情况修改都提示错误,但是报错的提示不同。

string3 = replaceString报错的原因和上面的一样。因为此时常量是string3,修改string3肯定报错。而修改*stirng3 = replaceString提示把指针对象replace赋值给了类*string3,类型不兼容错误。

这是我们想要达到的效果。


上面是定义对象类型的常量,下面我们再看看如何定义常量的int,float这种基本数据类型。 再看代码之间我们先来熟悉下指针变量,因为下面会用到这个概念。

指针变量

变量的指针就是变量的地址。存放变量地址的变量是指针变量。即在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个变量的地址或称为某变量的指针。

为了表示指针变量和它所指向的变量之间的关系,在程序中*表示“指向”,例如,ipointer代表指针变量,而*i_pointer是ipointer所指向的变量。因此,下面两个语句作用相同:

i=3;  
*i_pointer=3;

第2个语句的含义是将3赋给指针变量i_pointer所指向的变量。 定义指针变量

定义指针变量的一般形式为: 类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名是一个合法的标识符,类型说明符表示该指针变量所指向的变量的数据类型。例如:int *p1;表示p1是一个指针变量,它的值是某个整型变量的地址。或者说p1指向一个整型变量。至于p1究竟指向哪一个整型变量,应由向p1赋予的地址来决定。再如:

int *p2;  //p2是指向整型变量的指针变量  
float *p3;  //p3是指向浮点变量的指针变量  
char *p4;  //p4是指向字符变量的指针变量  

应该注意的是,一个指针变量只能指向同类型的变量,如 p3 只能指向浮点变量,不能时而指向一个浮点变量,时而又指向一个字符变量。

下面来看代码,我把解释也写在代码里面了。

/*
 const 右边的内容是常量,无法被修改
 */
const int age1 = 21;//等同于int const age1 = 21;  
int val1 = 103;  
age1 = 100; // 编译报错(age1是const右边的内容,所以修改就报错)*age1 = &val1;//编译报错


const int *age3 = NULL;//等同于int const *age4 = NULL; age3是指针变量,存放的是内存地址,*age3表示该内存地址指向的变量值  
int val1 = 101;  
*age3 = val1; // 编译报错,*age3在const右边,表示不可修改。此处是修改age3的值
age3 = &val1; // 编译通过,age3不在cosnt右边(要整体来看,不要单独看到age3也包括在*age3中),age3表示的是内存地址,此处修改age3的内存地址,也相当于修改了age3的地址,此时*age3等于101, age3为0x00007fff56879cec(每次都不同的)

int * const age5 = NULL;  
int val2 = 102;  
age5 = &val2; // 编译报错,age5在const右边,表示age5是常量,无法修改,故报错。此处修改的是age5的内存地址。  
*age5 = val2; // 编译通过,*age5不在const右边,所以不是常量,可被修改,此处修改的是age5的值


//    指针变量的赋值
    int a=10;
    int *p=&a;

//    或者
    int b=10;
    int *q;
    q=&b; //q表示存储值的内存地址
    *q=b;// *q表示内存地址指向的值

//    下面是错误的示范
    int *i = 10;

小结:

正确的常量定义如下:

NSString  *const  string = @"字符串";  
NSInteger const i = 1;

我们在定义常量的时候,会分成两种情况:

  • 局部常量,只在定义该常量的.m文件中使用
  • 全局常量,所有的.m文件都能使用

下面我们分别来看如何定义这两种常量


定义局部常量

定义局部常量非常简单,只需要在原先的常量定义前面加上一个static关键词即可,如下:

static NSString  *const  string = @"字符串";  
static NSInteger const i = 1;

定义全局常量

//1>自定义类Const,继承自NSObject

//2>在Const.m文件中
#import <Foundation/Foundation.h>
NSString *const constString = @"string";

//3>在Const.h文件中  
#import <Foundation/Foundation.h>  
extern NSString * const constString;

//4> 在pch文件中 #import "Const.h",这样项目里每一个.m文件都有 extern NSString * const constString;
PS:

定义外部变量的时候会用到如下几个关键词,区别如下: 有如下几个关键词:

extern:编译环境为纯oc  
UIKIT_EXTERN:兼容c和c++,oc  
FOUNDATION_EXPORT:同上  

comments powered by Disqus