在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(,); //调用函数
return ;
}

二、区别

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写在方法/函数中则作用域 只在此方法/函数中有效。
  • 若使用 typedef char * MyString; 则 MyString s1,s2等价于 char *s1; char *s2;
    若使用  #define MyString char * 则 MyString s1,s2等价于 char *s1,s2即 char *s1; char s2
    再次说明了typedef是类型替换,直接参与编译,而define只是简单的文本替换。

iOS学习——#define、const、typedef的区别的更多相关文章

  1. C++ —— 非类中使用const定义常量的初始化,以及#define和typedef的区别

    总结一下在非类中使用const关键字定义常量时的初始化问题,亲测VS2015.顺便记录#define宏和typedef的区别. 1 首先对const声明的常量的初始化做简单小结: , w2 = , w ...

  2. 【C语言笔记】#define与typedef的区别

    1.#define define是预处理指令,在编译时不进行任何检查,只进行简单的替换 宏定义的一般形式为: #define 宏名 字符串 这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符 ...

  3. define和typedef的区别

    define和typedef的区别 define是单纯的字符替换,typedef是重新定义了新的类型 #include <stdio.h> #define CHAR1 char* type ...

  4. #define const typedef

    #define用法 1. 定义简单的常数:定义常量,便于修改 #define N 1000 2. 定义简单的函数:注意多使用括号 define可以像函数那样接受一些参数,如下: #define max ...

  5. #define\const\inline的区别与联系

    总结: const用于代替#define一个固定的值,inline用于代替#define一个函数.是#define的升级版,为了消除#define的缺陷. 参考内容:http://www.cnblog ...

  6. #define 和typedef的区别

    typedef和define的详细区别 2011-04-19 15:11 firnow firnow 字号:T | T 对于都可以用来给对象取一个别名的Typedef和define来说,是有区别的.本 ...

  7. 宏定义#define和typedef的区别和典型范例题目辨析

    宏定义#define pStr char*  ,是直接把程序中出现pStr的地方替换成char* ,直接替换: typedef  char * pStr; 是给char*定义一个别名叫做 pStr; ...

  8. define与typedef的区别

    define: 发生在预处理阶段,也就是编译之前,仅仅文本替换,不做任何的类型检查 没有作用域的限制 typedef: 多用于简化复杂的类型声明,比如函数指针声明:typedef bool (*fun ...

  9. iOS学习笔记之typedef

    typedef unsigned long long weiboId; typedef 定义一个使用方便的类型,谓之为“宏定义“. unsigned long long 是一种无符号的长长整型.本应该 ...

随机推荐

  1. css选择器+、~、>

    在css中,选择器是一种模式,用于选择需要添加样式的元素.它有多种选择器,我们看一下比较常见,但容易混淆的三个选择器+.~.> A + B(相邻兄弟选择器) 定义:css2. 匹配B元素,满足条 ...

  2. Nginx的使用(一)代理静态文件

    1.为什要使用Nginx代理静态文件?这个问题去看静态资源快速加载. 2.作为后端服务器中间件,Tomcat是绝大多数Java程序员的选择.但是Tomcat处理请求的能力固然很强大,但是作为静态资源代 ...

  3. SpringBoot-异常问题总结

    一:创建的SpringBoot项目之后测试访问接口报错: Whitelabel Error Page This application has no explicit mapping for /err ...

  4. linux 启动springboot项目

    1.启动 nohup java -jar train-manager.jar --spring.profiles.active=test > out.log & 2.查看进程 ps -e ...

  5. buils tool是什么?为什么使用build tool?java主流的build tool

    定义: build tool是可以自动由源代码创建可执行的应用程序的程序. Building 包括编译.链接和打包代码成一个可用的或可执行形式. 在小型项目,开发人员常常会手动调用构建过程.在更大的项 ...

  6. 爬虫之scrapy-redis

    redis分布式部署 scrapy框架是否可以自己实现分布式? 不可以原因有两点 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的u ...

  7. 爬虫之正则和xpath

    一.正解解析 常用正则表达式回顾: 单字符: . : 除换行以外所有字符 [] :[aoe] [a-w] 匹配集合中任意一个字符 \d :数字 [-] \D : 非数字 \w :数字.字母.下划线.中 ...

  8. xpath路径定位

    preceding-sibling选择同级层所有节点向前查找 //div[@id='id1']/preceding-sibling::div/a 例如:“更多产”超链接的同级向上数第二个元素,即“登陆 ...

  9. 数据分析 大数据之路 五 pandas 报表

    pandas:  在内存中或对象,会有一套基于对象属性的方法,   可以视为 pandas 是一个存储一维表,二维表,三维表的工具, 主要以二维表为主 一维的表, (系列(Series)) 二维的表, ...

  10. [HACK] docker runtime 挂载宿主机目录

    网上看到的很多所谓的挂载都是容器创建时期的挂载,而且参数都不清不楚,整理如下(--name别名自己加): docker run -v /src/path:/dest/path:rw ${IMAGE} ...