iOS学习——#define、const、typedef的区别

  • 2019 年 10 月 8 日
  • 筆記

在iOS开发中经常遇到一些字段和类型的定义,例如配置生产和测试不同环境的参数等,这时候经常用到#define、const以及typedef。那么它们之间有什么区别呢?我们接下来一个一个具体了解下。

一、基本概念

1.1、#define

  #define并不是定义全局变量,而是宏定义。也就是说并不是真正意义上的定义变量,而是用来做文本替换。当程序开始运行时,编译器会先将代码中的MAX全部替换为100,然后再进行编译。由此可得,#define并不是在编译过程中进行,而是在预编译阶段进行。

#define MAX 100 

  宏的常见用法:

  • 常见的字符串抽成宏:比喻定义的常用颜色、字体字号等
#define kWaterAlpha 0.04f  //水印的透明度    #define kFlowRowSize 30  //流程每次拉去的数量    #define ROW_SIZE 20  //一般刷新每次拉去的数量
  • 常见代码抽成宏:比喻单例模板等
//宏定义常用的颜色  #define XRGB(r,g,b)     [UIColor colorWithRed:(0x##r)/255.0 green:(0x##g)/255.0 blue:(0x##b)/255.0 alpha:1]  #define kBgColor XRGB(f4, f4, f4)  #define kBlackFontColor XRGB(33, 33, 33)  #define kGrayFontColor XRGB(99, 99, 99)  #define kBlueFontColor XRGB(3d,9a,e8)    //宏定义获取当前主界面rootViewController  #define RootVC [UIApplication sharedApplication].delegate.window.rootViewController    //宏定义获取当前的界面  #define TopVC ([RootVC isKindOfClass:[UITabBarController class]]?[((UITabBarController *)RootVC).selectedViewController topViewController]:RootVC)    //宏定义单例的定义和实现  #define DECLARE_DEFAULT  +(instancetype)defaultInstance;    #define IMPLEMENT_DEFAULT(C)  +(instancetype)defaultInstance{  static dispatch_once_t onceToken;  static C *_gInstance;  dispatch_once(&onceToken, ^{  _gInstance = [C new];  });  return _gInstance;  }

  关于#define的其他用法可以参见后面这篇博文:iOS开发中你真的会用#define么!!!?

1.2、const

  关键字const用来定义常量,如果一个常量被const修饰,那么他的值就不能被改变。编译器通常不为普通const常量分配存储空间,而是保存于符号表中,这使得它成为一个编译期间的常量,没有存储与读内存的操作,使得它的效率更高。

  常见用法如下:

//全局变量,constString1地址不能修改,constString1值能修改  const NSString *constString1 = @"I am a const NSString * string";  //意义同上,无区别  NSString const *constString2 = @"I am a NSString const * string";  // stringConst 地址能修改,stringConst值不能修改  NSString * const stringConst = @"I am a NSString * const string";
  • constString1 跟constString2 无区别
  • 左边代表指针本身的类型信息,const表示这个指针指向的这个地址是不可变的
  • 右边代表指针指向变量的可变性,即指针存储的地址指向的内存单元所存储的变量的可变性

1.3、typedef

  typedef常用于给类型起别名(给已知的类型起别名)。常用于简化复杂类型,变量类型意义化等。typedef是类型替换,语句的一种,结尾必须有;。

//iOS底层源码就是对NSInteger进行了一个别名的设置,其表示的就是long或者int类型。  #if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64      typedef long NSInteger;      typedef unsigned long NSUInteger;  #else      typedef int NSInteger;      typedef unsigned int NSUInteger;  #endif

  在iOS开发中最常用到的应该就是使用typedef定义枚举和block了,此外还可以用typedef定义函数。关于typedef定义枚举官方API是这样说的

/* NS_ENUM supports the use of one or two arguments. The first argument is always the integer type used for the values of the enum. The second argument is an optional type name for the macro. When specifying a type name, you must precede the macro with 'typedef' like so:  NS_ENUM支持单个或两个参数,第一个参数一般是NSInteger类型来制定枚举的值类型,第二个参数是可选的枚举类型的别名,如果要定义别名,必须使用typedef进行定义,具体格式如下:    typedef NS_ENUM(NSInteger, NSComparisonResult) {      ...  };    If you do not specify a type name, do not use 'typedef'. For example:  如果不需要使用特定的名称,则不需要使用typedef    NS_ENUM(NSInteger) {      ...  };  */

  typedef的常见用法如下:

typedef double NSTimeInterval;  //给double取别名为NSTimeInterval(变量类型意义化)  typedef NSTimeInterval MyTime;  //给NSTimeInterval取别名为MyTime  typedef char * MyString;  //给char *取别名为MyString    //c语言格式,给Person结构体取别名为MyPerson。使用:MyPerson p = {"jack"};  typedef struct Person {      char *name  }MyPerson;    //c语言格式,给Gender枚举取别名为MyGender。使用:MyGender g = Man;  typedef enum Gender {      Man,      Woman  }MyGender;    //OC语言格式,给Gender枚举取别名为MyGender。使用:MyGender g = Man;  typedef NS_ENUM(NSInteger, Gender) {      Man,      Woman  };    //给block取别名MyBlock  typedef void(^MyBlock) (int a,int b);    //给指向函数的指针取别名MyFunction  typedef int(*MyFunction) (int a,int b); 

typedef定义函数的示例:

int add (int a, int b){       return a + b;  }    typedef int(*MyMethod) (int a,int b);    int main(){      MyMethod m = add;      m(5,6);   //调用函数      return 0;  }

二、区别

2.1 #define与const

  • 宏在预编译时处理(宏在编译开始之前就会被替换);而const会在编译时被处理
  • #define宏没有类型,宏不做任何类型检查,不会报编译错误,只是替换;而const常量有具体的类型,会编译检查,会报编译错误
  • 宏能定义一些函数,方法;const不能
  • 使用大量宏,容易造成编译时间久,每次都需要重新替换
  • 宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。);而const常量会在内存中分配(可以是堆中也可以是栈中),const 可以节省空间,避免不必要的内存分配

const

#define PI 3.14159 //常量宏

const doulbe Pi=3.14159; //此时并未将Pi放入ROM中

double I=PI; //预编译期间进行宏替换,分配内存

double i=Pi; //此时为Pi分配内存,以后不再分配!

double J=PI; //再进行宏替换,又一次分配内存

double j=Pi; //没有内存分配

  • const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝(因为是全局的只读变量,存在静态区),而 #define定义的常量在内存中有若干个拷贝。
  • 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

2.2 typedef和#define

  • define是文本替换,属于预编译指令,本身不参与编译,除非希望替换的文本中有;否则不用加。 typedef是类型替换,语句的一种,结尾必须有;
  • define写在方法/函数中则作用域从写的地方开始有效,直至使用#undef(不写此指令则后面一直有效)。typedef写在方法/函数中则作用域 只在此方法/函数中有效。
  • 若使用 则 MyString s1,s2等价于 char *s1; char *s2; 若使用 #define MyString char * 则 MyString s1,s2等价于 char *s1,s2char *s1; char s2 再次说明了typedef是类型替换,直接参与编译,而define只是简单的文本替换。

typedef char * MyString;