宏(define)与常量(const)
既然选择了远方,便只顾风雨兼程。
宏(define)
一. 宏的理解
宏是一种批量处理的称谓。一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串)。这种替换在预编译时进行,称作宏展开。编译器会在编译前扫描代码,如果遇到我们已经定义好的宏那么就会进行代码替换,宏只会在内存中copy一份,然后全局替换,宏一般分为对象宏和函数宏。
宏的优点:
- 提高了程序的可读性,同时也方便进行修改,用户只需要在一处定义,多处使用,修改也只需要修改一处;
宏的缺点:
- 只在预处理里做文本替换,没有类型,不做类型检查。大量使用宏会导致二进制文件变大,会使编译时间变长;
二. 对象宏
语法示例: #define M_PI 3.141592
,项目中常用的对象宏如下所示。
#ifndef HBObjectMacro_h
#define HBObjectMacro_h
// 获取iOS版本号
#define kIOSVersions [[[UIDevice currentDevice] systemVersion] floatValue]
// 获取window
#define kUIWindow [[[UIApplication sharedApplication] delegate] window]
// 获取屏幕宽
#define kSCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
// 获取屏幕高
#define kSCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
// 获取状态栏高度(20、44)
#define kHeight_StatusBar [[UIApplication sharedApplication] statusBarFrame].size.height
// 获取导航栏高度
#define kHeight_NavigationBar 44.f
// 获取导航栏加状态栏高度
#define kHeight_NavBar (kHeight_StatusBar > 20?88.f:64.f)
// 获取非安全区高度
#define kHeight_NoSafeArea (kHeight_StatusBar > 20?34.f:0.f)
// 获取Tabbar高度加非安全区高度
#define kHeight_Tabbar (kHeight_StatusBar > 20?83.f:49.f)
// 判断是否为iPhone
#define IS_IPHONE ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
// 判断是否为iPad
#define IS_IPAD ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
// 判断是否为iPod
#define IS_IPOD ([[[UIDevice currentDevice] model] isEqualToString: @"iPod touch"])
// 弱引用__weak
#define kWeakObj(weakName,objName) __weak typeof(&*objName)weakName = objName;
// 强引用__strong
#define kStrongObj(strongName,objName) __strong typeof(&*objName)strongName = objName;
// 数据持久化
#define kUserDefaults [NSUserDefaults standardUserDefaults]
// 通知中心
#define kNotificationCenter [NSNotificationCenter defaultCenter]
// 设置图片基础方法
#define kUIImage(imageName) [UIImage imageNamed:imageName]
// 设置字体基础方法
#define kUIFont(size) [UIFont systemFontOfSize:size]
// 设置字体基础方法
#define kUIBoldFont(size) [UIFont boldSystemFontOfSize:size]
// 设置随机颜色
#define kRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0]
// 设置RGB颜色
#define kRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
// 设置RGBA颜色
#define kRGBAColor(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(r)/255.0 blue:(r)/255.0 alpha:a]
// 这是clear背景颜色
#define kClearColor [UIColor clearColor]
#endif /* HBObjectMacro_h */
三. 函数宏
函数宏的作用就类似于一个函数一样,函数一般代码量比较多,这时如果函数宏过长,我们可以使用\
进行换行处理,这样提升了代码的可读性。
#define IS_LEAP_YEAR2(y) y%4==0&&y%100!=0 \
||y%400==0
四. 预编译指令和运算符
接下来我们了解下宏定义中常用的预编译指令和运算符。
4.1 预编译指令
# 空指令,无任何效果
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
4.2 常用运算符
-
\
换行处理。 -
#
运算符:在OC中使用字符串都需要使用@"",如果想直接使用字符串可以添加一个#
运算符。出现在宏定义中的#
运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#
称为字符串化运算符。# define Test(n) "Test"#n // 调用: Test(Demo); // 打印:TestDemo
-
##
运算符:用于将相邻的两个标记(Token)连接为一个。使用时先分隔(根据空格或其他操作分隔符[+,-,*,/,”,”等]),再强制连接(去掉和前面的字符串间的空格,再连接起来)。#define Data(a, b, c) a##b##c NSLog(@"%d", Data(1, 2, 3)); // 123
-
@#
:字符化操作符,只能用于有参数传入的宏定义中,必须置于宏定义体参数名前,作用是将传入的单字符参数名转换成字符,以一对单引号括起来。
五. 预定义宏和可变参数宏
5.1 C语言中预定义宏
__FILE__ :当前源代码的文件名(字符串)
__LINE__:当前源代码中的行号(整型)
__DATE__:进行预处理的日期(”Mmm dd yyyy”形式的字符串)
__TIME__:源文件编译时间(格式“hh:mm:ss”)
__FUNCTION__:同__func__(但IDE不支持),当前源代码的函数名
__PRETTY_FUNCTION__:同__FUNCITON__,但在g++下会输类名、函数名及其他函数信息
例:
5.2. 可变参数宏
可变参数宏:#define DBGMSG(format, ...) fprintf (stderr, format, __VA_ARGS__)
,…
表示一个可变化的参数表,变参必须放于最后一个参数;
__VA_ARGS__
:是一个可变参数的宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##
的作用在于,当可变参数的个数为0时,这里的##
起到把前面多余的","去掉的作用,否则会编译出错;
#ifdef DEBUG
#define NSLog(format, ...) NSLog(@"%s(%d): " format, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define NSLog(...)
#endif
常量(const)
在开发中,当我们想定义全局共用的一些数据时,比如通知名字,服务器的地址等等,我们经常使用用到宏定义、变量,或者用const常量
来修饰这些数据类型。然而经常有开发者不知道怎么正确使用这些修饰符,导致项目中乱用宏定义与const修饰符。那么我们该怎么正确选择使用呢?参考苹果API的使用,建议尽量使用const来定义,为什么要这么做呢?下面我们先从基本概念方面来了解下他们之间的联系和区别。
-
宏:用法,一般字符串抽成宏,代码抽成宏使用。宏只是在预处理阶段进行文本替换,没有类型,不做任何类型检查,编译器可以对相同的字符串进行优化,只保存一份到数据段。甚至有相同后缀的字符串也可以优化,你可以使用GCC编译测试,
Hello world
与world
两个字符串,只存储前面一个。取的时候只需要给前面和中间的地址,如果是整型、浮点型会有多分拷贝,但这些数写在指令中,占的只是代码片段而且,大量使用宏会导致二进制文件变大。#define URL @"http://www.xx.xx"
-
变量:共享一块内存空间,就算项目中多处用到,也不会分配多块内存空间,可以被修改,在编译阶段做类型检查。
NSString *url = @"http://www.xx.xx";
-
常量:共享一块内存空间,就算项目中多处用到,也不会分配多块内存空间,可以根据
const
修饰的位置设定能够修改,在编译阶段做类型检查。一般常用的字符串定义成const(对于常量字符串苹果推荐我们使用const)。-
常量区分:
-
全局常量:不管你定义在任何文件夹,外部都能访问;
// ViewController.m文件中 定义全局常量 const NSString *url = @"http://www.xx.xx"; // AppDelegate.m文件中 访问全局变量 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. // 访问全局常量 extern NSString *url; NSLog(@"008 - 宏定义 - 访问全局常量 = %@",url); return YES; } // 输出:2021-09-06 15:44:20.151408+0800 BaseGrammar[95798:357990] 008 - 宏定义 - 访问全局常量 = http://www.xx.xx
-
局部常量:用static修饰后,不能供外界访问;
static const NSString *url = @"http://www.xx.xx";
-
-
const位置不同,代表什么:
// 写法一:*url不能被修改,url能被修改 const NSString *url = @"http://www.xx.xx"; // 写法二:*url不能被修改,url能被修改 NSString const *url = @"http://www.xx.xx"; // 写法三:url不能被修改,*url能被修改 NSString * const url = @"http://www.xx.xx";
由以上代码我们可以总结为:
const
右边的总不能被修改。所以我们一般定义一个常量又不想被修改,应该这样定义:NSString * const url = @"http://www.xx.xx";
-
常量的规范使用,一般项目里,定义全局常量,会写在独立文件里:
// HBConst.m文件定义常量 #import "HBConst.h" // 定义常量 /** 网络请求地址*/ NSString * const HBUrl = @"http://www.xx.xx"; /** cell间距 */ CGFloat const HBCellMargin = 4.0;
// HBConst.h文件提供外接访问常量 /** 网络请求地址*/ UIKIT_EXTERN NSString * const HBUrl; /** cell间距 */ UIKIT_EXTERN CGFloat const HBCellMargin;
宏与const区别:
-
- 编译时刻不同,宏
(define)
属于预编译,在预处理阶段进行替换;const
常量在编译阶段使用; - 宏
(define)
可以定义代码(如函数),const
不可以; - 宏
(define)
不做类型检查,只进行替换,const
常量有数据类型,会执行类型检查; - 宏
(define)
定义的常量在替换后运行过程中,会不断占用内存,而const
定义的常量存储在数据段,只有一份拷贝,效率更高;
最后建议,我们以后在开发中如果定义一个常量字符串就用const,定义代码就用宏。
原文地址:https://www.cnblogs.com/hubert-style/p/15234942.html
- nginx利用geo模块做限速白名单以及geo实现全局负载均衡的操作记录
- Mysql高效插入/更新数据
- 宋小菜融资2.3亿元!域名保护意识强
- 世界最奇葩的7款机器人
- Mysql高效插入/更新数据
- 关于Membership/Role您可能不知道的细节
- Sqlite向MySql导入数据
- 未来3年,人工智能如何影响法律行业?5位权威专家给出趋势
- Java 常见内存溢出异常与代码实现
- nginx限制上传大小和超时时间设置说明/php限制上传大小
- Unity Application Block 1.2 学习笔记
- 苹果首个自动驾驶专利到底有什么来头?
- 围棋遇上互联网:科技打开优秀传统文化未来之门
- 神经网络开始放飞自我!都是因为架构搜索新算法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- WPF 如何确定应用程序开启了 Pointer 触摸消息的支持
- Hexo-Matery主题美化
- uniapp onPullDownRefresh 下拉刷新小坑
- Windows服务器常用配置
- SAP CRM订单数据库表CRMD_SHIPPING的填充原理
- 使用art-template模板引擎渲染数据
- SAP CRM索引数据库表CRMD_ORDER_INDEX的更新原理
- 将自己的nodeJS项目分享到npm上
- react中使用prop-types检测props数据类型
- git的常用命令及工作中冲突问题解决方法
- 关于react中的context
- 基于Node.js的Express框架
- react官方推荐的classnames库
- 在Deno中构建一个命令行天气预报程序
- react-router4