iOS深思篇 | 宏定义
一. 简介
宏是一种批量处理的称谓,简单来说就是根据定义好的规则替换一定的文本。替换过程在程序编译期,也因此大量使用宏会造成编译时间变长;而且替换过程不进行类型安全检查;还需要注意“边缘效应”;
比如#define N 1 + 2
,使用时NSInteger a = N / 2
, 预期1.5
,结果是2
,因为在处理过程中转化为NSInteger a = 1 + 2 / 2
,所以建议使用宏时加括号表明是一个整体。
想要了解宏的话得先了解一下来源,OC从C语言演变来,自然也继承了C语言的优良传统,这里简单介绍一下,C语言中预处理命令,它包括三个方面:
- 宏定义:#define 指令定义一个宏,#undef指令删除一个宏定义。
- 文件包含:#include指令指定一个文件的内容被包含到程序中。
- 条件编译:#if,#ifdef,#ifndef,#elif,#else和#endif指令可以根据编译器可以测试的条件来将一段文本包含到程序中或排除在程序之外。
需要注意的是预处理命令都是以符号“#”开头。
1.1 宏的分类
大部分将宏按类型分为对象宏和函数宏,也有按传入参数分为带参数的宏和不带参数的宏。
1.1.1 对象宏
#define STATUS_HEIGHT 20
复制代码
1.1.2 函数宏
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
复制代码
1.1 宏定义与常量定义的区别
#define
与const
都可用来修饰常量。
- 编译器处理方式不同 define宏是在预处理阶段展开。 const常量是编译运行阶段使用。
- 类型和安全检查不同 define宏没有类型,不做任何类型检查,仅仅是展开。 const常量有具体的类型,在编译阶段会执行类型检查。
- 存储方式不同 define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。) const常量会在内存中分配(可以是堆中也可以是栈中)。
- const可以节省空间,避免不必要的内存分配。
- 提高了效率;编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
- 宏替换只作替换,不做计算,不做表达式求解;
1.2 宏的一些用法
1.2.1 字符化
将传入的单字符参数名转换成字符,以一对单引用括起来.
#define STRING @#s // 's'
复制代码
1.2.2 字符串化
在宏参数前加个#,那么在宏体扩展的时候,宏参数会被扩展成字符串的形式。
#define NSSTRING #str // "str"
复制代码
1.2.3 连接
如果宏体所在标示符中有##,那么在宏体扩展的时候,宏参数会被直接替换到标示符中。
#define COMMAND(PREFIX, NAME) PREFIX##NAME
复制代码
1.2.4 换行
遇到需要换行的可以用\号连接;
#define PRINT_IF(CONDITION) \
do { if (CONDITION) \
NSLog(@"print hello"); } \
while (0)
复制代码
1.2.5 变参宏(…
和_VA_ARGS
)
下面是OC中自定义Log的例子:
#ifdef DEBUG
#define Log(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define Log(...)
#endif
复制代码
需要注意的是: __VA_ARGS__
: 至少传一个参数 ##__VA_ARGS__
: 随便传几个参数
二. C语言的宏
下面是一些C语言的宏:
#define 定义一个预处理宏
#undef 取消宏的定义
#include 包含文件命令
#include_next 与#include相似, 但它有着特殊的用途
#if 编译预处理中的条件命令, 相当于C语法中的if语句
#ifdef 判断某个宏是否被定义, 若已定义, 执行随后的语句
#ifndef 与#ifdef相反, 判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
#defined 与#if, #elif配合使用, 判断某个宏是否被定义
#line 标志该语句所在的行号
# 将宏参数替代为以参数值为内容的字符窜常量
## 将两个相邻的标记(token)连接为一个单独的标记
#pragma 说明编译器信息
#warning 显示编译警告信息
#error 显示编译错误信息
复制代码
是不是感觉很熟悉,就算在iOS开发中也经常用到里面的内容。 除了基本的宏操作还有预定义宏,预定义宏是为了方便处理一些有用的信息,里面定义了一些预处理标识符,也就是预定义宏。预定义宏的名称都是以“__”(两条下划线)开头和结尾的,如果宏名是由两个单词组成,那么中间以“_”(一条下划线)进行连接。并且,宏名称一般都由大写字符组成。 下面是常见的预定义宏:
宏 | 描 述 |
---|---|
FUNTION | 获取当前函数名 |
DATE | 丐前源文件的编泽口期,用 “Mmm dd yyy”形式的字符串常量表示 |
FILE | 当前源文件的名称,用字符串常量表示 |
LINE | 当前源义件中的行号,用十进制整数常量表示,它可以随#line指令改变 |
TIME | 当前源文件的最新编译吋间,用“hh:mm:ss”形式的宁符串常量表示 |
STDC | 如果今前编泽器符合ISO标准,那么该宏的值为1,否则未定义 |
COUNTER | 无重复的计数器,从程序启动开始每次调用都会++,常用语宏中定义无重复的参数名称 |
func | 所在scope的函数名称,常见于log中 |
三. OC相关宏的扩展
3.1 系统相关宏
宏 | 描 述 |
---|---|
__has_include | 用来检查是否引入了某个文件 |
NS_ASSUME_NONNULL_BEGIN & NS_ASSUME_NONNULL_END | 在这两个宏之间的代码,所有简单指针对象都被假定为nonnull |
__cplusplus | 识别是c代码还是c++代码 |
__has_feature(objc_arc) | 判断是否是ARC,否则为MRC |
@available(iOS 11, *) | 当前iOS11是否满足需求 |
TARGET_IPHONE_SIMULATOR | 满足条件时,执行模拟器代码;否则执行非模拟器代码 |
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 | 设备系统大于8.0 以上的代码 |
NS_REQUIRES_SUPER | 申明子类如果重写该方法,必须调用该父类方法 |
FOUNDATION_EXPORT | 用于定义常量,在检测值是否相等时直接比较指针,效率比较快 |
NS_AVAILABLE_IOS(8_0) | 这个方法可以在iOS3.0及以后的版本中使用,如果在比5.0更老的版本中调用这个方法,就会引起崩溃 |
NS_DEPRECATED_IOS(2_0, 6_0) | 这个方法在iOS2.0引入,6.0被删除 |
NS_AVAILABLE(10_8, 6_0) | 这个宏告诉我们这方法分别随Mac OS 10.8和iOS 6.0被引入 |
NS_DEPRECATED(10_0, 10_6, 2_0, 4_0) | 这个方法随Mac OS 10.0和iOS 2.0被引入,在Mac OS 10.6和iOS 4.0后被废弃 |
NS_CLASS_AVAILABLE(10_11, 9_0) | 这个类分别随Mac OS 10.11和iOS9.0被引入 |
NS_ENUM_AVAILABLE(10_11, 9_0) | 这个枚举分别随Mac OS 10.11和iOS9.0被引入 |
__IPHONE_OS_VERSION_MAX_ALLOWED | 允许最大的iOS版本 |
__IPHONE_OS_VERSION_MIN_ALLOWED | 最低的iOS版本 |
3.2 自定义的宏
/**** UI尺寸 ****/
//获取屏幕宽度与高度
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREENH_HEIGHT [UIScreen mainScreen].bounds.size.height
//根据6,7,8适配
#define ScaleWidth(width) (width / 375.0) * SCREEN_WIDTH
#define ScaleHeight(height) (height / 667.0) * SCREENH_HEIGHT
//是否是iPhoneX
#define k1IS_iPhoneX (SCREEN_WIDTH == 375.f && SCREENH_HEIGHT == 812.f)
#define k2IS_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)//判断是否为X系列
#define IPHONE_X \
({BOOL isPhoneX = NO;\
if (@available(iOS 11.0, *)) {\
isPhoneX = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0;\
}\// 状态栏高度
#define kStatusBarHeight (IPHONE_X ? 44.f : 20.f)
// 顶部导航栏高度
#define kNavigationBarHeight 44.f
// 顶部安全距离
#define kSafeAreaTopHeight (IPHONE_X ? 88.f : 64.f)
// 底部安全距离
#define kSafeAreaBottomHeight (IPHONE_X ? 34.f : 0.f)
// Tabbar高度
#define kTabbarHeight 49.f
// 去除上下导航栏剩余中间视图高度
#define ContentHeight (kScreenHeight - kSafeAreaTopHeight - kSafeAreaBottomHeight - kTabbarHeight)/**** 颜色 ****/
//随机颜色
#define ZBRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0]
//RGB
#define ZBRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
//RGBA
#define ZBRGBAColor(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(r)/255.0 blue:(r)/255.0 alpha:a]
//十六进制颜色
#define ZBRGBHex(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
//十六进制颜色,透明度
#define ZBRGBHexAlpha(rgbValue,a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:(a)]/**** 系统相关 ****/
//app版本号
#define DEVICE_APP_VERSION (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]
//app Build版本号
#define DEVICE_APP_BUILD (NSString *)[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]
//系统版本号(string)
#define DEVICE_OS_VERSION [[UIDevice currentDevice] systemVersion]
//系统版本号(float)
#define DEVICE_OS_VERSION_VALUE [DEVICE_OS_VERSION floatValue]
//检测是否是竖屏状态
#define IsPortrait ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait || [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown)/**** 沙盒目录文件 ****/
//temp
#define ZBPathTemp NSTemporaryDirectory()
//Document
#define ZBPathDocument [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
//Cache
#define ZBPathCache [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]/**** 数据判空 ****/
//字符串是否为空
#define kStringIsEmpty(str) ([str isKindOfClass:[NSNull class]] || str == nil || [str length] < 1 ? YES : NO )
//数组是否为空
#define kArrayIsEmpty(array) (array == nil || [array isKindOfClass:[NSNull class]] || array.count == 0)
//字典是否为空
#define kDictIsEmpty(dic) (dic == nil || [dic isKindOfClass:[NSNull class]] || dic.allKeys == 0)
//是否是空对象
#define kObjectIsEmpty(_object) (_object == nil \
|| [_object isKindOfClass:[NSNull class]] \
|| ([_object respondsToSelector:@selector(length)] && [(NSData *)_object length] == 0) \
|| ([_object respondsToSelector:@selector(count)] && [(NSArray *)_object count] == 0))/**** 常用缩写 ****/
#define kApplication [UIApplication sharedApplication]
#define kKeyWindow [UIApplication sharedApplication].keyWindow
#define kAppDelegate [UIApplication sharedApplication].delegate
#define kUserDefaults [NSUserDefaults standardUserDefaults]
#define kNotifCenter [NSNotificationCenter defaultCenter]/**** 其他 ****/
//弱引用
#define ZBWeak __weak typeof(self) weakSelf = self;
#define ZBWeakSelf(type) __weak typeof(type) weak##type = type;
//强引用
#define ZBStrongSelf(type) __strong typeof(type) type = weak##type;//角度转换弧度
#define ZBDegreesToRadian(x) (M_PI * (x) / 180.0)
//弧度转换角度
#define ZBRadianToDegrees(radian) (radian*180.0)/(M_PI)//block判空回调
#define ZBBlockNotEmpt(block, ...) if (block) { block(__VA_ARGS__); }//.h头文件中的单例宏
#define ZBSingletonH(name) + (instancetype)shared##name;//.m文件中的单例宏
#define ZBSingletonM(name) \
static id _instance;\
+ (instancetype)allocWithZone:(struct _NSZone *)zone{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
+ (instancetype)shared##name{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [[self alloc] init];\
});\
return _instance;\
}\
- (id)copyWithZone:(NSZone *)zone{\
return _instance;\
}
复制代码
四. 总结
介绍了这么多宏的相关知识,最后总结下对宏的几点感想:
- 宏直接调用方法名或者常量名称的方式易于理解,可以减少重复代码,统一规范,方便修改;
- 使用太多宏会增加编译时长,而且还需注意“边缘效应”,防止发生不可预期的错误;
- 定义宏时应遵守规范,比如宏名和参数的括号间不能有空格;定义表达式要外面用括号包裹等;
暂时先说这么多,后续还将继续更新,最后欢迎大佬们下方吹水。
学习:
GCC Macros
宏--从入门到精通
【如何正确使用const,static,extern】|那些人追的干货
const常量与define宏定义的区别
宏定义的黑魔法 - 宏菜鸟起飞手册
C语言编译预处理和条件编译执行过程的理解
C语言宏定义的几个坑和特殊用法
C语言中宏定义的使用
深入理解C语言中宏定义
转载于:https://juejin.im/post/5cebce946fb9a07ece67aec4
iOS深思篇 | 宏定义相关推荐
- c语言 define宏名称if(参数),iOS深思篇 | 宏定义
一. 简介 宏是一种批量处理的称谓,简单来说就是根据定义好的规则替换一定的文本.替换过程在程序编译期,也因此大量使用宏会造成编译时间变长:而且替换过程不进行类型安全检查;还需要注意"边缘效应 ...
- 如何识别 macos/ios 使用跨平台宏定义
在xcode中系统内置的跨平台宏定义在头文件中 #ifdef #ifdef __APPLE__ #include <TargetConditionals.h> #endif 此头文件中各个 ...
- ios 常用的宏定义
#pragma mark - shortcuts #ifdef QA # define NSLog(fmt, ...) NSLog((@"[文件名:%s]\n" "[函数 ...
- 宏定义:带参数的宏名和为空的宏体
在wxwidgets中遇到这样的一个宏 1 /* Macro to cut down on compiler warnings. */2 #if 1 /* there should be no mor ...
- iOS - 常用宏定义
iOS中的常用宏定义 此篇博客为博主转载经典文章,非常感谢原创的优秀资源! 为方便常看和使用,在此我将原文中的宏定义在此以代码片形式展示出来,具体内容如下: #ifndef MacroDefiniti ...
- Flutter iOS问题记录 - 多环境配置下Pod库的宏定义失效
文章目录 前言 开发环境 问题描述 问题分析 1. 创建用于测试的Pod库 2. 验证问题是否只存在于Pod库 3. __OPTIMIZE__在什么时候会定义 4. 影响__OPTIMIZE__定义的 ...
- iOS 开发 高级:使用 宏定义macros (#,##,...,__VA_ARGS_)
一直以来用宏定义#define也就是定义一些简单的常量,至多也就是定义一个函数,很少关注宏定义的用法.直到看到这样的代码: [cpp] view plaincopy #define PLAYSOUND ...
- iOS日常工作之常用宏定义大全
前言: 在工作中, 很多小伙伴都会在PCH文件定义一些常用的宏,但是又怕写这些简单的宏浪费时间,又有时候忘记怎么定义了怎么办?本人在工作中也是如此.所以在这里给大家分享一些常用的宏定义,喜欢的小伙伴可 ...
- iOS常用宏定义大全
宏定义与常量的区别 宏:只是在预处理器里进行文本替换,不做任何类型检查,宏能定义代码,const不能,多个宏编译时间相对较长,影响开发效率,调试过慢,const只会编译一次,缩短编译时间. 所以在使用 ...
最新文章
- Android 动画的插值器 (Interpolator属性)
- 基于注解的 IOC 配置
- shell中字符串基本用法
- 助力航天元器件管理“高可靠降成本”,赛思库获数千万元Pre-A轮融资
- Java数组(1)--数组概述
- Xception,Inception-ResNet,SENet(Squeeze-and-Excitation)
- 马上开课 | 临床基因组学数据分析实战助力解析Case,快速发表文章
- 整理Java基础知识--Date Time2
- SAP傻瓜式安装教程
- 浅析智慧消防巡检系统在高速公路隧道的应用
- 基于SprnigBoot+ElementUI 整合Vue案例【公司案件管理系统】
- e-Learning e-learning 创造竞争优势
- 淘宝店铺的装修是店铺的门面,如何进行淘宝店铺装修?需要注意的点有哪些?
- 景深的计算及弥散圆、光圈的概念
- erp框架 saas_Saas模式ERP系统
- 简单易行的用windows系统虚拟苹果mac操作系统 mac下android环境搭建笔记(android studio)
- 模式识别 | PRML概览
- python图片自动上色_老旧黑白片修复机——使用卷积神经网络图像自动着色实战(附PyTorch代码)...
- 论文投稿指南——中文核心期刊推荐(力学)
- 内存的分配与回收实验
热门文章
- linux终端 php清屏,Linux 清屏命令
- dell服务器查看内存详细信息,DELL R710服务器内存故障排错
- 我宣布:这是我心中录音转文字APP中NO.1
- 小白踩坑记:spring初始化时出现:factory-bean reference points back to the same bean definition
- 长春财经学院计算机专业多少分录取,长春财经学院2020录取分数线(附2017-2020年分数线)...
- Java代码实现sha256加密
- .net毕业总结:学会举一反三你才能省时而高效,一通百通!
- vscode 左下方状态栏不显示 python 环境信息
- 拆分数字:Excel函数不求人
- 哪款蓝牙耳机佩戴舒适度比较高?2022四款音质蓝牙耳机推荐